diff --git a/.gitignore b/.gitignore index 51ebf9e9..776ebe10 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ # Log file *.log - +logs/* # BlueJ files *.ctxt @@ -27,3 +27,4 @@ hs_err_pid* /target/ /src/main/resources/static/ +certificates diff --git a/DOCKERFILE b/DOCKERFILE index 96bc29bf..f2a2ffa0 100644 --- a/DOCKERFILE +++ b/DOCKERFILE @@ -1,3 +1,6 @@ +#很久没维护了,已经与定前版本不匹配 + + FROM ubuntu:20.04 AS build ARG DEBIAN_FRONTEND=noninteractive @@ -84,8 +87,8 @@ RUN echo '#!/bin/bash' > run.sh && \ echo 'nohup java -jar *.jar --userSettings.record=/opt/media/www/record/ &' >> run.sh && \ echo 'nohup /opt/media/MediaServer -d -m 3 &' >> run.sh && \ echo 'cd /opt/wvp' >> run.sh && \ - echo 'if [${WVP_CONFIG}]; then' >> run.sh && \ - echo ' java -jar *.jar --spring.confi g.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 ${WVP_CONFIG}' >> run.sh && \ + echo 'if [-n "${WVP_CONFIG}"]; then' >> run.sh && \ + echo ' java -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 ${WVP_CONFIG}' >> run.sh && \ echo 'else' >> run.sh && \ echo ' java -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 --media.ip=127.0.0.1 --media.sdp-ip=${WVP_IP} --sip.ip=${WVP_IP} --media.stream-ip=${WVP_IP}' >> run.sh && \ echo 'fi' >> run.sh diff --git a/README.md b/README.md index e7645c60..b2eaf748 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -![logo](https://gitee.com/pan648540858/wvp-GB28181-pro/raw/wvp-28181-2.0/web_src/static/logo.png) -# 开箱即用的的28181协议视频平台 +![logo](doc/_media/logo.png) +# 开箱即用的28181协议视频平台 [![Build Status](https://travis-ci.org/xia-chu/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xia-chu/ZLMediaKit) [![license](http://img.shields.io/badge/license-MIT-green.svg)](https://github.com/xia-chu/ZLMediaKit/blob/master/LICENSE) @@ -8,16 +8,16 @@ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-yellow.svg)](https://github.com/xia-chu/ZLMediaKit/pulls) -WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。支持国标级联,支持rtsp/rtmp等视频流转发到国标平台,支持rtsp/rtmp等推流转发到国标平台。 +WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR接入。支持国标级联,支持将不带国标功能的摄像机/直播流/直播推流转发到其他国标平台。 -流媒体服务基于ZLMediaKit-https://github.com/xiongziliang/ZLMediaKit - -前端页面基于MediaServerUI进行修改. +流媒体服务基于@夏楚 ZLMediaKit [https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit) +播放器使用@dexter jessibuca [https://github.com/langhuihui/jessibuca/tree/v3](https://github.com/langhuihui/jessibuca/tree/v3) +前端页面基于@Kyle MediaServerUI [https://gitee.com/kkkkk5G/MediaServerUI](https://gitee.com/kkkkk5G/MediaServerUI) 进行修改. # 应用场景: 支持浏览器无插件播放摄像头视频。 支持摄像机、平台、NVR等设备接入。 -支持国标级联。 +支持国标级联。多平台级联。跨网视频预览。 支持rtsp/rtmp等视频流转发到国标平台。 支持rtsp/rtmp等推流转发到国标平台。 @@ -25,66 +25,55 @@ WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的开箱即用的网 旨在打造一个易配置,易使用,便于维护的28181国标信令系统, 依托优秀的开源流媒体服务框架ZLMediaKit, 实现一个完整易用GB28181平台. # 部署文档 -[https://github.com/648540858/wvp-GB28181-pro/wiki](https://github.com/648540858/wvp-GB28181-pro/wiki) +[doc.wvp-pro.cn](https://doc.wvp-pro.cn) # gitee同步仓库 https://gitee.com/pan648540858/wvp-GB28181-pro.git # 截图 -![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_1.png) -![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_2.png) -![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_20201012_151459.png) -![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_20201012_152643.png) -![build_1.png](https://github.com/648540858/wiki/blob/master/images/Screenshot_20201012_151606.png) +![index](doc/_media/index.png "index.png") +![2](doc/_media/2.png "2.png") +![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") -# 1.0 基础特性 -1. 视频预览; -2. 云台控制(方向、缩放控制); -3. 视频设备信息同步; -4. 离在线监控; -5. 录像查询与回放(基于NVR\DVR,暂不支持快进、seek操作); -6. 无人观看自动断流; -7. 支持UDP和TCP两种国标信令传输模式; -8. 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署, 随wvp一起部署; -9. 支持平台接入, 针对大平台大量设备的情况进行优化; -10. 支持检索,通道筛选; -11. 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题; -12. 支持启用udp多端口模式, 提高udp模式下媒体传输性能; -13. 支持通道是否含有音频的设置; -14. 支持通道子目录查询; -15. 支持udp/tcp国标流传输模式; -16. 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址 -17. 支持国标网络校时 -18. 支持公网部署, 支持wvp与zlm分开部署 -19. 支持播放h265, g.711格式的流(需要将closeWaitRTPInfo设为false) -20. 报警信息处理,支持向前端推送报警信息 - -# 1.0 新支持特性 -1. 集成web界面, 不需要单独部署前端服务, 直接利用wvp内置文件服务部署, 随wvp一起部署; -2. 支持平台接入, 针对大平台大量设备的情况进行优化; -3. 支持检索,通道筛选; -4. 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题; -5. 支持启用udp多端口模式, 提高udp模式下媒体传输性能; -6. 支持通道是否含有音频的设置; -7. 支持通道子目录查询; -8. 支持udp/tcp国标流传输模式; -9. 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址 -10. 支持国标网络校时 -11. 支持公网部署, 支持wvp与zlm分开部署 -12. 支持播放h265, g.711格式的流 -13. 支持固定流地址和自动点播,同时支持未点播时直接播放流地址,代码自动发起点播. ( [查看WIKI](https://github.com/648540858/wvp-GB28181-pro/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E5%9B%BA%E5%AE%9A%E6%92%AD%E6%94%BE%E5%9C%B0%E5%9D%80%E4%B8%8E%E8%87%AA%E5%8A%A8%E7%82%B9%E6%92%AD)) -14. 报警信息处理,支持向前端推送报警信息 -15. 支持订阅与通知方法 - - [X] 移动位置订阅 - - [X] 移动位置通知处理 - - [X] 报警事件订阅 - - [X] 报警事件通知处理 - - [ ] 设备目录订阅 - - [X] 设备目录通知处理 -16. 移动位置查询和显示,可通过配置文件设置移动位置历史是否存储 - -# 2.0 支持特性 -- [X] 国标通道向上级联 +# 功能特性 +- [X] 集成web界面 +- [X] 兼容性良好 +- [X] 支持电子地图,支持接入WGS84和GCJ02两种坐标系,并且自动转化为合适的坐标系进行展示和分发 +- [X] 接入设备 + - [X] 视频预览 + - [X] 无限制接入路数,能接入多少设备只取决于你的服务器性能 + - [X] 云台控制,控制设备转向,拉近,拉远 + - [X] 预置位查询,使用与设置 + - [X] 查询NVR/IPC上的录像与播放,支持指定时间播放与下载 + - [X] 无人观看自动断流,节省流量 + - [X] 视频设备信息同步 + - [X] 离在线监控 + - [X] 支持直接输出RTSP、RTMP、HTTP-FLV、Websocket-FLV、HLS多种协议流地址 + - [X] 支持通过一个流地址直接观看摄像头,无需登录以及调用任何接口 + - [X] 支持UDP和TCP两种国标信令传输模式 + - [X] 支持UDP和TCP两种国标流传输模式 + - [X] 支持检索,通道筛选 + - [X] 支持通道子目录查询 + - [X] 支持过滤音频,防止杂音影响观看 + - [X] 支持国标网络校时 + - [X] 支持播放H264和H265 + - [X] 报警信息处理,支持向前端推送报警信息 + - [X] 支持订阅与通知方法 + - [X] 移动位置订阅 + - [X] 移动位置通知处理 + - [X] 报警事件订阅 + - [X] 报警事件通知处理 + - [X] 设备目录订阅 + - [X] 设备目录通知处理 + - [X] 移动位置查询和显示 + - [X] 支持手动添加设备和给设备设置单独的密码 +- [X] 支持平台对接接入 +- [X] 支持国标级联 + - [X] 国标通道向上级联 - [X] WEB添加上级平台 - [X] 注册 - [X] 心跳保活 @@ -95,33 +84,47 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git - [X] 平台状态查询 - [X] 平台信息查询 - [X] 平台远程启动 -- [X] 添加RTSP视频 -- [X] 添加接口鉴权 -- [ ] 添加ONVIF探测局域网内的设备 -- [X] 添加RTMP视频 -- [X] 云端录像(需要部署单独服务配合使用) + - [X] 每个级联平台可自定义的虚拟目录 + - [X] 目录订阅与通知 + - [X] 录像查看与播放 + - [X] GPS订阅与通知(直播推流) +- [X] 支持自动配置ZLM媒体服务, 减少因配置问题所出现的问题; - [X] 多流媒体节点,自动选择负载最低的节点使用。 -- [X] 支持使用mysql作为数据库,默认sqlite3,开箱即用。 -- [ ] 添加系统配置 -- [ ] 添加用户管理 -- [X] WEB端支持播放H264与H265,音频支持G.711A/G.711U/AAC,覆盖国标常用编码格式。 +- [X] 支持启用udp多端口模式, 提高udp模式下媒体传输性能; +- [X] 支持公网部署; +- [X] 支持wvp与zlm分开部署,提升平台并发能力 +- [X] 支持拉流RTSP/RTMP,分发为各种流格式,或者推送到其他国标平台 +- [X] 支持推流RTSP/RTMP,分发为各种流格式,或者推送到其他国标平台 +- [X] 支持推流鉴权 +- [X] 支持接口鉴权 +- [X] 云端录像,推流/代理/国标视频均可以录制在云端服务器,支持预览和下载 + -# docker快速体验 -```shell -docker pull 648540858/wvp_pro - -docker run --env WVP_IP="你的IP" -it -p 18080:18080 -p 30000-30500:30000-30500/udp -p 30000-30500:30000-30500/tcp -p 80:80 -p 5060:5060 -p 5060:5060/udp 648540858/wvp_pro -``` -docker使用详情查看:[https://hub.docker.com/r/648540858/wvp_pro](https://hub.docker.com/r/648540858/wvp_pro) - -# gitee同步仓库 -https://gitee.com/pan648540858/wvp-GB28181-pro.git +# 遇到问题如何解决 +国标最麻烦的地方在于设备的兼容性,所以需要大量的设备来测试,目前作者手里的设备有限,再加上作者水平有限,所以遇到问题在所难免; +1. 查看wiki,仔细的阅读可以帮你避免几乎所有的问题 +2. 搜索issues,这里有大部分的答案 +3. 加QQ群(901799015),这里有大量热心的小伙伴,但是前提新希望你已经仔细阅读了wiki和搜索了issues。 +4. 你可以请作者为你解答,但是我不是免费的。 +5. 你可以把遇到问题的设备寄给我,可以更容易的复现问题。 # 使用帮助 -QQ群: 901799015, 690854210(ZLM大群) +QQ群: 901799015, ZLM使用文档[https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit) QQ私信一般不回, 精力有限.欢迎大家在群里讨论.觉得项目对你有帮助,欢迎star和提交pr。 +# 授权协议 +本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议 # 致谢 -感谢作者[夏楚](https://github.com/xia-chu) 提供这么棒的开源流媒体服务框架 +感谢作者[夏楚](https://github.com/xia-chu) 提供这么棒的开源流媒体服务框架,并在开发过程中给予支持与帮助。 +感谢作者[dexter langhuihui](https://github.com/langhuihui) 开源这么好用的WEB播放器。 +感谢作者[Kyle](https://gitee.com/kkkkk5G) 开源了好用的前端页面 +感谢各位大佬的赞助以及对项目的指正与帮助。包括但不限于代码贡献、问题反馈、资金捐赠等各种方式的支持!以下排名不分先后: +[lawrencehj](https://github.com/lawrencehj) [Smallwhitepig](https://github.com/Smallwhitepig) [swwhaha](https://github.com/swwheihei) +[hotcoffie](https://github.com/hotcoffie) [xiaomu](https://github.com/nikmu) [TristingChen](https://github.com/TristingChen) +[chenparty](https://github.com/chenparty) [Hotleave](https://github.com/hotleave) [ydwxb](https://github.com/ydwxb) +[ydpd](https://github.com/ydpd) [szy833](https://github.com/szy833) [ydwxb](https://github.com/ydwxb) [Albertzhu666](https://github.com/Albertzhu666) +[mk1990](https://github.com/mk1990) [SaltFish001](https://github.com/SaltFish001) + +ps: 刚增加了这个名单,肯定遗漏了一些大佬,欢迎大佬联系我添加。 diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100644 index 00000000..0f3c4c97 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +###################################################### +# Copyright 2019 Pham Ngoc Hoai +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Repo: https://github.com/tyrion9/spring-boot-startup-script +# +######### PARAM ###################################### + +JAVA_OPT=-Xmx1024m +JARFILE=`ls -1r *.jar 2>/dev/null | head -n 1` +PID_FILE=pid.file +RUNNING=N +PWD=`pwd` + +######### DO NOT MODIFY ######## + +if [ -f $PID_FILE ]; then + PID=`cat $PID_FILE` + if [ ! -z "$PID" ] && kill -0 $PID 2>/dev/null; then + RUNNING=Y + fi +fi + +start() +{ + if [ $RUNNING == "Y" ]; then + echo "Application already started" + else + if [ -z "$JARFILE" ] + then + echo "ERROR: jar file not found" + else + nohup java $JAVA_OPT -Djava.security.egd=file:/dev/./urandom -jar $PWD/$JARFILE > nohup.out 2>&1 & + echo $! > $PID_FILE + echo "Application $JARFILE starting..." + tail -f nohup.out + fi + fi +} + +stop() +{ + if [ $RUNNING == "Y" ]; then + kill -9 $PID + rm -f $PID_FILE + echo "Application stopped" + else + echo "Application not running" + fi +} + +restart() +{ + stop + start +} + +case "$1" in + + 'start') + start + ;; + + 'stop') + stop + ;; + + 'restart') + restart + ;; + + *) + echo "Usage: $0 { start | stop | restart }" + exit 1 + ;; +esac +exit 0 + diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 00000000..0fb5b86d --- /dev/null +++ b/doc/README.md @@ -0,0 +1,102 @@ +# 介绍 + +> 开箱即用的28181协议视频平台 + +# 概述 +- 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以及平台。 +- 支持将国标设备级联到其他国标平台,也支持将不支持国标的设备的图像或者直播推送到其他国标平台 +- 前端完善,自带完整前端页面,无需二次开发可直接部署使用。 +- 完全开源,且使用MIT许可协议。保留版权的情况下可以用于商业项目。 +- 支持多流媒体节点负载均衡。 + +# 我们实现了哪些国标功能 +**作为上级平台** +- [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/cascade1.png b/doc/_content/ability/_media/cascade1.png new file mode 100644 index 00000000..9ba8280b Binary files /dev/null and b/doc/_content/ability/_media/cascade1.png differ diff --git a/doc/_content/ability/_media/cascade2.png b/doc/_content/ability/_media/cascade2.png new file mode 100644 index 00000000..4dd62cf2 Binary files /dev/null and b/doc/_content/ability/_media/cascade2.png differ diff --git a/doc/_content/ability/_media/cascade3.png b/doc/_content/ability/_media/cascade3.png new file mode 100644 index 00000000..f95f5d5a Binary files /dev/null and b/doc/_content/ability/_media/cascade3.png differ diff --git a/doc/_content/ability/_media/cascade4.png b/doc/_content/ability/_media/cascade4.png new file mode 100644 index 00000000..9db85b55 Binary files /dev/null and b/doc/_content/ability/_media/cascade4.png differ diff --git a/doc/_content/ability/_media/img.png b/doc/_content/ability/_media/img.png new file mode 100644 index 00000000..6a0c550d Binary files /dev/null and b/doc/_content/ability/_media/img.png differ diff --git a/doc/_content/ability/_media/img_1.png b/doc/_content/ability/_media/img_1.png new file mode 100644 index 00000000..31995c3c Binary files /dev/null and b/doc/_content/ability/_media/img_1.png differ diff --git a/doc/_content/ability/_media/img_10.png b/doc/_content/ability/_media/img_10.png new file mode 100644 index 00000000..030502d0 Binary files /dev/null and b/doc/_content/ability/_media/img_10.png differ diff --git a/doc/_content/ability/_media/img_11.png b/doc/_content/ability/_media/img_11.png new file mode 100644 index 00000000..cb0f3d51 Binary files /dev/null and b/doc/_content/ability/_media/img_11.png differ diff --git a/doc/_content/ability/_media/img_12.png b/doc/_content/ability/_media/img_12.png new file mode 100644 index 00000000..d6fe8777 Binary files /dev/null and b/doc/_content/ability/_media/img_12.png differ diff --git a/doc/_content/ability/_media/img_13.png b/doc/_content/ability/_media/img_13.png new file mode 100644 index 00000000..6be1128f Binary files /dev/null and b/doc/_content/ability/_media/img_13.png differ diff --git a/doc/_content/ability/_media/img_14.png b/doc/_content/ability/_media/img_14.png new file mode 100644 index 00000000..24712045 Binary files /dev/null and b/doc/_content/ability/_media/img_14.png differ diff --git a/doc/_content/ability/_media/img_15.png b/doc/_content/ability/_media/img_15.png new file mode 100644 index 00000000..f167811d Binary files /dev/null and b/doc/_content/ability/_media/img_15.png differ diff --git a/doc/_content/ability/_media/img_16.png b/doc/_content/ability/_media/img_16.png new file mode 100644 index 00000000..f7ce9e7a Binary files /dev/null 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 new file mode 100644 index 00000000..02b303e3 Binary files /dev/null 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 new file mode 100644 index 00000000..5ca4faf9 Binary files /dev/null and b/doc/_content/ability/_media/img_18.png differ diff --git a/doc/_content/ability/_media/img_2.png b/doc/_content/ability/_media/img_2.png new file mode 100644 index 00000000..f9f2f55f Binary files /dev/null and b/doc/_content/ability/_media/img_2.png differ diff --git a/doc/_content/ability/_media/img_3.png b/doc/_content/ability/_media/img_3.png new file mode 100644 index 00000000..efe688cf Binary files /dev/null and b/doc/_content/ability/_media/img_3.png differ diff --git a/doc/_content/ability/_media/img_4.png b/doc/_content/ability/_media/img_4.png new file mode 100644 index 00000000..f548cec7 Binary files /dev/null and b/doc/_content/ability/_media/img_4.png differ diff --git a/doc/_content/ability/_media/img_5.png b/doc/_content/ability/_media/img_5.png new file mode 100644 index 00000000..6959c780 Binary files /dev/null and b/doc/_content/ability/_media/img_5.png differ diff --git a/doc/_content/ability/_media/img_6.png b/doc/_content/ability/_media/img_6.png new file mode 100644 index 00000000..04c42bc1 Binary files /dev/null and b/doc/_content/ability/_media/img_6.png differ diff --git a/doc/_content/ability/_media/img_7.png b/doc/_content/ability/_media/img_7.png new file mode 100644 index 00000000..1a8edbf0 Binary files /dev/null and b/doc/_content/ability/_media/img_7.png differ diff --git a/doc/_content/ability/_media/img_8.png b/doc/_content/ability/_media/img_8.png new file mode 100644 index 00000000..02fa66f1 Binary files /dev/null and b/doc/_content/ability/_media/img_8.png differ diff --git a/doc/_content/ability/_media/img_9.png b/doc/_content/ability/_media/img_9.png new file mode 100644 index 00000000..708e901a Binary files /dev/null and b/doc/_content/ability/_media/img_9.png differ diff --git a/doc/_content/ability/auto_play.md b/doc/_content/ability/auto_play.md new file mode 100644 index 00000000..5259e0b5 --- /dev/null +++ b/doc/_content/ability/auto_play.md @@ -0,0 +1,2 @@ + +# 自动点播 diff --git a/doc/_content/ability/cascade.md b/doc/_content/ability/cascade.md new file mode 100644 index 00000000..7942bda4 --- /dev/null +++ b/doc/_content/ability/cascade.md @@ -0,0 +1,34 @@ + +# 国标级联的使用 +国标28181不同平台之间支持两种连接方式,平级和上下级,WVP目前支持向上级级联。 +## 1 接入平台 +### 1.1 wvp-pro +#### 1.1.1 wvp-pro管理页面点击添加 + ![cascade1](_media/cascade1.png) +#### 1.1.2 填入wvp-pro上级平台信息 + ![cascade1](_media/img_4.png) + ![cascade1](_media/img_5.png) +#### 1.1.3 编辑wvp-pro上级设备信息,开启订阅 + ![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) + +## 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 new file mode 100644 index 00000000..7bd6c792 --- /dev/null +++ b/doc/_content/ability/cascade2.md @@ -0,0 +1,18 @@ + +# 国标级联的使用 +国标28181不同平台之间支持两种连接方式,平级和上下级,WVP目前支持向上级级联。 +## 添加上级平台 +在国标级联页面点击“添加”按钮,以推送到上级WVP为例子,参看[接入设备](./_content/ability/device.md) +![cascade17](_media/img_17.png) +点击保存可以在上级的国标通道列表看到新增加的设备; +国标级联列表出现了级联的这个平台;同时状态显示为在线,如果状态为离线那么可能是你的服务信息配置有误或者网络不通。 +订阅信息列有三个图标,表示上级开启订阅,从左到右依次是:报警订阅,目录订阅,移动位置订阅。 +## 推送通道 +点击你要推送的平台的“选择通道”按钮。 +![cascade18](_media/img_18.png) +- **页面结构** + - 左侧为目录结构 + 选择未分配,则右侧显示待分配的通道,可以点击“添加按钮”,在弹窗中选择要放置的位置,保存后即可添加通道成功 + 选择其他的目录可以看到已经分配在这个目录下的通道,可以对其进行删除后重新在未分配中去分配。 + - 右侧为数据展示以及操作 + 国标通道栏内为来自其他国标设备/平台的通道;直播流通道为来自推流/拉流代理的通道。 \ No newline at end of file diff --git a/doc/_content/ability/cloud_record.md b/doc/_content/ability/cloud_record.md new file mode 100644 index 00000000..4ac4f8f1 --- /dev/null +++ b/doc/_content/ability/cloud_record.md @@ -0,0 +1,8 @@ + +# 云端录像 +云端录像是对录制在zlm服务下的录像文件的管理,录像的文件路径默认在ZLM/www/record下,使用云端录像功能必须部署wvp-pro-assist,主要通过调用wvp-pro-assist的接口完成各种功能。 +如果你需要24小时的录像,目前有一个这种方案,可以参考[7*24不间断录像](./_content/ability/continuous_recording.md)。 +1. 云段录像支持录像文件的查看,播放(可能因为编码的原因导致无法播放); +2. 支持录像的下载; +3. 支持录像的合并下载; +功能没有太多特殊的地方就不一一介绍了,大家自行体验吧。 diff --git a/doc/_content/ability/continuous_recording.md b/doc/_content/ability/continuous_recording.md new file mode 100644 index 00000000..516830fd --- /dev/null +++ b/doc/_content/ability/continuous_recording.md @@ -0,0 +1,14 @@ + +# 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 new file mode 100644 index 00000000..ceed545e --- /dev/null +++ b/doc/_content/ability/device.md @@ -0,0 +1,36 @@ + +# 接入设备 +设备接入主要是需要在设备上配置28181上级也就是WVP-PRO的信息,只有信息一致的情况才可以注册成功。设备注册成功后打开WVP->国标设备,可以看到新增加的设备;[设备使用](./_content/ability/device_use.md), +主要有以下字段需要配置: + +- sip->ip +本机IP,不要使用127.0.0.1/0.0.0.0, 除非你对项目及其熟悉 + +- sip->port +28181服务监听的端口 + +- sip->domain +domain宜采用ID统一编码的前十位编码。 + +- sip->id +28181服务ID + +- sip->password +28181服务密码 + +- 配置信息在如下位置 + +![_media/img_16.png](_media/img_16.png) +*** +## 大华摄像头 +![_media/img_10.png](_media/img_10.png) +## 大华NVR +![_media/img_11.png](_media/img_11.png) +## 艾科威视摄像头 +![_media/img_15.png](_media/img_15.png) +## 水星摄像头 +![_media/img_12.png](_media/img_12.png) +## 海康摄像头 +![_media/img_9.png](_media/img_9.png) + +[设备使用](_content/ability/device_use.md) diff --git a/doc/_content/ability/device_use.md b/doc/_content/ability/device_use.md new file mode 100644 index 00000000..c406b3bb --- /dev/null +++ b/doc/_content/ability/device_use.md @@ -0,0 +1,35 @@ + +# 设备使用 +### 更新设备通道 + 点击列表末尾的“刷新”按钮,可以看到一个圆形进度条,等进度结束提示成功后即可更新完成,如果通道数量有变化你可以看点击左上角的![刷新](_media/img_14.png)即可看到通道数量的变化;如果通道数量仍未0,那么可能时对方尚未推送通道给你。 +### 查看设备通道 + 点击列表末尾的“通道”按钮, +### 查看设备定位 + 点击列表末尾的“定位”按钮,即可跳转到地图页面看到设备的位置 +### 编辑设备在WVP中一些功能 +点击列表末尾的“编辑”按钮,即可在打开的弹窗中对设备功能进行修改 +- 设备名称 + 如何未能从设备里读取到设备名称或者需要自己重命名,那么可以修改此选项。 +- 字符集 + 修改读取设备数据时使用的字符集,默认为GB2312,但是GB2312收录的汉字不全,所以有时候回遇到乱码,可以修改为UTF-8来解决。 +- 地理坐标系 + 展示此设备定位信息时使用的设用什么坐标系来解析经纬度,一般不用修改,如果遇到定位不准,可以修改尝试修改此选项解决。 +- 目录结构 + 展示设备的通道信息时,使用设备作为树形结构的依据,国标28181定义了两种树形结构,详情查看[国标28181的树形结构](./_content/theory/channel_tree.md); +- 目录订阅 + 填写订阅周期即可对设备开启目录订阅,设备如果支持目录订阅那么设备在通道信息发生变化时就会通知WVP哪些通道发生了那些变化,包括通道增加/删除/更新/上线/下线/视频丢失/故障。0为取消订阅。 + 一般NVR和平台对接可以开启此选项,直接接摄像机开启此选项意义不大。 +- 移动位置订阅 + 对设备开启移动位置订阅,设备如果支持目录订阅那么设备位置发生变化时会通知到WVP,一般执法记录仪可以开启此选项,对固定位置的设备意义不大。 +- SSRC校验 + 为了解决部分设备出现的串流问题,可以打开此选项。ZLM会严格按照给定的ssrc处理视频流。部分设备流信息不标准,开启可能导致无法点播。 +### 删除设备 + 可以删除WVP中的设备信息,如果设备28181配置未更改,那么设备在下一次注册后仍然会注册上来。 +### 点播视频 + 进入通道列表后,点击列表末尾的“播放”按钮,稍等即可弹出播放页面 +### 设备录像 + 进入通道列表后,点击列表末尾的“设备录像”按钮,也可以在播放页面点击录像查询进入录像查看页面,选择要查看的日期即可对录像进行播放和下载。 +### 云台控制 + 可以对支持云台功能的设备进行上下左右的转动以及拉近拉远的操作。 +### 获取视频的播放器地址 + 视频点播成功后在实时视频页面,点击“更多地址”可以看到所有的播放地址,地址是否可以播放与你是否完整编译启用zlm功能有关,更与网络有关。 \ No newline at end of file diff --git a/doc/_content/ability/gis.md b/doc/_content/ability/gis.md new file mode 100644 index 00000000..8796dba0 --- /dev/null +++ b/doc/_content/ability/gis.md @@ -0,0 +1,37 @@ + +# 电子地图 +WVP提供了简单的电子地图用于设备的定位以及移动设备的轨迹信息,电子地图基于开源的地图引擎openlayers开发。 +### 查看设备定位 +1. 可以在设备列表点击“定位”按钮,自动跳转到电子地图页面; +2. 在电子地图页面在设备上右键点击“定位”获取设备/平台下的所有通道位置。 +3. 单击通道信息可以定位到具体的通道 + + +### 查询设备轨迹 +查询轨迹需要提前配置save-position-history选项开启轨迹信息的保存,目前WVP此处未支持分库分表,对于大数据量的轨迹信息无法胜任,有需求请自行二次开发或者定制开发。 +在电子地图页面在设备上右键点击“查询轨迹”获取设备轨迹信息。 + +PS: 目前的底图仅用用作演示和学习,商用情况请自行购买授权使用。 + +### 更换底图以及底图配置 +目前WVP支持使用了更换底图,配置文件在web_src/static/js/mapConfig.js,请修改后重新编译前端文件。 +```javascript +window.mapParam = { + // 开启/关闭地图功能 + enable: true, + // 坐标系 GCJ-02 WGS-84, + coordinateSystem: "GCJ-02", + // 地图瓦片地址 + tilesUrl: "http://webrd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8", + // 瓦片大小 + tileSize: 256, + // 默认层级 + zoom:10, + // 默认地图中心点 + center:[116.41020, 39.915119], + // 地图最大层级 + maxZoom:18, + // 地图最小层级 + minZoom: 3 +} +``` diff --git a/doc/_content/ability/node_manger.md b/doc/_content/ability/node_manger.md new file mode 100644 index 00000000..1b4aef32 --- /dev/null +++ b/doc/_content/ability/node_manger.md @@ -0,0 +1,9 @@ + +# 节点管理 +WVP支持单个WVP多个ZLM的方案来扩展WVP的视频并发能力,并发点播是因为带宽和性能的原因,单个ZLM节点能支持的路数有限,所以WVP增加了ZLM集群来扩展并发并且保证ZLM的高可用。 +## 默认节点 +WVP中为了保证功能的完整性,ZLM节点至少要有一个默认节点,这个节点不是在管理页面添加的,而是在WVP的配置文件中配置的,这个节点不可在页面删除。每次启动会自动从配置文件中读取配置写入数据库备用。 +## 新增节点 +启动你要添加的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 new file mode 100644 index 00000000..55ecb268 --- /dev/null +++ b/doc/_content/ability/online_doc.md @@ -0,0 +1,2 @@ + +# 在线文档 diff --git a/doc/_content/ability/proxy.md b/doc/_content/ability/proxy.md new file mode 100644 index 00000000..74e632d8 --- /dev/null +++ b/doc/_content/ability/proxy.md @@ -0,0 +1,24 @@ + +# 拉流代理 +不是所有的摄像机都支持国标或者推流的,但是这些设备可以得到一个视频播放地址,通常为rtsp协议, +以大华为例: +```text +rtsp://{user}:{passwd}@{ipc_ip}:{rtsp_port}/cam/realmonitor?channel=1&subtype=0 +``` +可以得到这样一个流地址,可以直接用vlc进行播放,此时我们可以通过拉流代理功能将这个设备推送给其他国标平台了。 +流程如下: +```plantuml +@startuml +"摄像机" <- "ZLMediaKit": 1. 流去流信息到ZLM +"ZLMediaKit" -> "WVP-PRO": 2. 收到hook通知得到流信息 +"上级国标平台" -> "WVP-PRO": 3. 点播这路视频 +"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配置文件中添加一个即可。 diff --git a/doc/_content/ability/push.md b/doc/_content/ability/push.md new file mode 100644 index 00000000..ef501826 --- /dev/null +++ b/doc/_content/ability/push.md @@ -0,0 +1,41 @@ + +# 推流列表 +## 功能说明 + +WVP支持三种图像输入方式,直播,[拉流代理](_content/ability/proxy.md),[国标](_content/ability/device.md),直播设备接入流程如下 +```plantuml +@startuml +"直播设备" -> "ZLMediaKit": 1. 发起推流 +"ZLMediaKit" -> "WVP-PRO": 2. 收到hook通知得到流信息 +"上级国标平台" -> "WVP-PRO": 3. 点播这路视频 +"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) + +## 推拉流鉴权规则 +为了保护服务器的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 new file mode 100644 index 00000000..0c472386 --- /dev/null +++ b/doc/_content/ability/user.md @@ -0,0 +1,2 @@ + +# 用户管理 diff --git a/doc/_content/about_doc.md b/doc/_content/about_doc.md new file mode 100644 index 00000000..f727264e --- /dev/null +++ b/doc/_content/about_doc.md @@ -0,0 +1,5 @@ + + +# 关于本文档 +本文档开源在gitee上,[https://gitee.com/pan648540858/wvp-pro-doc.git](https://gitee.com/pan648540858/wvp-pro-doc.git),如果文档出现任何错误或者不易理解的语句,请大家提ISSUE帮助我及时更正。欢迎大家提交PR一起维护这份文档,让更多的人可以使用到这个开源的视频平台。 + diff --git a/doc/_content/disclaimers.md b/doc/_content/disclaimers.md new file mode 100644 index 00000000..94412ecd --- /dev/null +++ b/doc/_content/disclaimers.md @@ -0,0 +1,2 @@ +# 免责声明 +WVP-PRO自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议 \ No newline at end of file diff --git a/doc/_content/introduction/_media/img.png b/doc/_content/introduction/_media/img.png new file mode 100644 index 00000000..48f8d8e4 Binary files /dev/null and b/doc/_content/introduction/_media/img.png differ diff --git a/doc/_content/introduction/_media/img_1.png b/doc/_content/introduction/_media/img_1.png new file mode 100644 index 00000000..aad1859d Binary files /dev/null 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 new file mode 100644 index 00000000..e7869000 Binary files /dev/null and b/doc/_content/introduction/_media/img_2.png differ diff --git a/doc/_content/introduction/compile.md b/doc/_content/introduction/compile.md new file mode 100644 index 00000000..b208685b --- /dev/null +++ b/doc/_content/introduction/compile.md @@ -0,0 +1,95 @@ + +# 编译 +WVP-PRO不只是实现了国标28181的协议,本身也是一个完整的视频平台。所以对于新手来说,你可能需要一些耐心来完成。遇到问题不要焦躁,你可以 +1. 百度 +2. 如果身边有熟悉java的朋友,可以咨询下朋友; +3. 来群里(901799015)咨询群友; +4. 向作者发送邮件648540858@qq.com; +5. 作者远程支持(有偿)。 + 如果这些仍不能解决你的问题,那么你可能需要与作者我一起合作完成这个项目,解决你遇到的问题。 + + +WVP-PRO使用Spring boot开发,maven管理依赖。对于熟悉spring开发的朋友是很容易进行编译部署以及运行的。 +下面将提供一种通用方法方便大家运行项目。 +## 1 服务介绍 +| 服务 | 作用 | 是否必须 | +|----------------|------------------------------------------|-------------------------| +| WVP-PRO | 实现国标28181的信令以及视频平台相关的功能 | 是 | +| ZLMediaKit | 为WVP-PRO提供国标28181的媒体部分的实现,以及各种视频流格式的分发支持 | 是 | +| wvp-pro-assist | wvp的辅助录像程序,也可单独跟zlm一起使用,提供录像控制,录像合并下载接口 | 否(不安装只是影响云端录像功能和国标录像下载) | + +## 2 安装依赖 +| 依赖 | 版本 | 用途 | 开发环境需要 | 生产环境需要 | +|--------|------------|-------------|--------|--------| +| 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),截取一下关键步骤: +```bash +# 国内用户推荐从同步镜像网站gitee下载 +git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit +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.npm.taobao.org install +npm run build +``` +编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 +编译完成后在src/main/resources下出现static目录 +**编译完成一般是这个样子,中间没有报红的错误信息** +![编译成功](_media/img.png) + +### 5.3 打包项目, 生成可执行jar +```bash +cd wvp-GB28181-pro +mvn package +``` +编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 +编译完成后在target目录下出现wvp-pro-***.jar。 +接下来[配置服务](./_content/introduction/config.md) + + + + + + + + diff --git a/doc/_content/introduction/config.md b/doc/_content/introduction/config.md new file mode 100644 index 00000000..ba8d564e --- /dev/null +++ b/doc/_content/introduction/config.md @@ -0,0 +1,120 @@ + +# 配置 +对于首次测试或者新手同学,我建议在局域网测试,并且关闭服务器与客户机的防火墙测试。建议部署在linux进行测试。 + +```plantuml +@startuml +"WVP-PRO" -> "ZLMediaKit": RESTful 接口 +"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。 +### 1.1 默认加载配置文件方式 +使用maven打包后的jar包里,已经存在了配置文件,但是每次打开jar包修改配置文件或者修改后再打包都是比较麻烦的,所以大家可通过指定配置文件路径来加载指定位置的配置文件。 +```shell +cd wvp-GB28181-pro/target +java -jar wvp-pro-*.jar --spring.config.location=../src/main/resources/application.yml +``` +### 1.2 迁移配置文件以方便启动 +由于配置文件的命令比较长,所以为了启动方便通常我会把配置文件放到jar包的同级目录,类似这样, +移除jar包内/BOOT-INF/classes/下所有以application开头的文件,使用解压缩工具打开jar即可,不需要解压出来。 +```shell +cd wvp-GB28181-pro/target +mv ../src/main/resources/application-dev.yml application.yml +java -jar wvp-pro-*.jar +``` +这也是我自己最常用的方式。 +## 2 配置WVP-PRO +### 2.1 Mysql数据库配置 +首先你需要创建一个名为wvp(也可使用其他名字)的数据库,并使用sql/mysql.sql导入数据库,初始化数据库结构。 +(这里注意,取决于版本,新版的sql文件夹下有update.sql,补丁包,一定要注意运行导入) +在application-dev.yml中配置(使用1.2方式的是在jar包的同级目录的application.yml)配置数据库连接,包括数据库连接信息,密码。 +### 2.2 Redis数据库配置 +配置wvp中的redis连接信息,建议wvp自己单独使用一个db。 +### 2.3 配置服务启动端口(可直接使用默认配置) +```yaml +# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +server: + port: 18080 +``` +### 2.4 配置28181相关信息(可直接使用默认配置) +```yaml +# 作为28181服务器的配置 +sip: + # [必须修改] 本机的IP + ip: 192.168.1.3 + # [可选] 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: + # ZLMediaKit的服务ID,必须配置 + id: FQ3TF8yT83wh5Wvz + # [必须修改] zlm服务器的内网IP,sdp-ip与stream-ip使用默认值的情况下,这里不要使用127.0.0.1/0.0.0.0 + ip: 192.168.1.3 + # [必须修改] zlm服务器的http.port + http-port: 6080 + # [可选] zlm服务器的hook.admin_params=secret + secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 + rtp: + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 + enable: true + # [可选] 在此范围内选择端口用于媒体流传输, + port-range: 30000,30500 # 端口范围 + # [可选] 国标级联在此范围内选择端口发送媒体流, + send-port-range: 30000,30500 # 端口范围 + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 + record-assist-port: 18081 +``` +### 2.4 个性化定制信息配置 +```yaml +# [根据业务需求配置] +user-settings: + # [可选] 服务ID,不写则为000000 + server-id: + # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true + auto-apply-play: false + # [可选] 部分设备需要扩展SDP,需要打开此设置 + senior-sdp: false + # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) + save-position-history: false + # 点播等待超时时间,单位:毫秒 + play-timeout: 3000 + # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 + wait-track: false + # 是否开启接口鉴权 + interface-authentication: true + # 自动配置redis 可以过期事件 + redis-config: true + # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 + interface-authentication-excludes: + - /api/v1/** + # 推流直播是否录制 + record-push-live: true + # 国标是否录制 + record-sip: true + # 是否将日志存储进数据库 + logInDatebase: true + # 第三方匹配,用于从stream钟获取有效信息 + thirdPartyGBIdReg: [\s\S]* +``` + + +如果配置信息无误,你可以启动zlm,再启动wvp来测试了,启动成功的话,你可以在wvp的日志下看到zlm已连接的提示。 +接下来[部署到服务器](./_content/introduction/deployment.md), 如何你只是本地运行直接再本地运行即可。 diff --git a/doc/_content/introduction/deployment.md b/doc/_content/introduction/deployment.md new file mode 100644 index 00000000..388fd357 --- /dev/null +++ b/doc/_content/introduction/deployment.md @@ -0,0 +1,36 @@ + + +# 部署 +**请仔细阅读以下内容** +1. WVP-PRO与ZLM支持分开部署,但是wvp-pro-assist必须与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 | 多端口开放 | + +3. 测试环境部署建议所有服务部署在一台主机,关闭防火墙,减少因网络出现问题的可能; +4. WVP-PRO与ZLM支持分开部署,但是wvp-pro-assist必须与zlm部署在同一台主机; +5. 生产环境按需开放端口,但是建议修改默认端口,尤其是5060端口,易受到攻击; +6. zlm使用docker部署的情况,要求端口映射一致,比如映射5060,应将外部端口也映射为5060端口; +7. 启动服务,以linux为例 +**启动WVP-PRO** +```shell +nohup java -jar wvp-pro-*.jar & +``` + +**启动ZLM** +```shell +nohup ./MediaServer -d -m 3 & +``` + +[接入设备](./_content/ability/device.md) + diff --git a/doc/_content/qa/_media/img.png b/doc/_content/qa/_media/img.png new file mode 100644 index 00000000..d6c29d71 Binary files /dev/null and b/doc/_content/qa/_media/img.png differ diff --git a/doc/_content/qa/_media/img_1.png b/doc/_content/qa/_media/img_1.png new file mode 100644 index 00000000..5b74d95b Binary files /dev/null and b/doc/_content/qa/_media/img_1.png differ diff --git a/doc/_content/qa/_media/img_2.png b/doc/_content/qa/_media/img_2.png new file mode 100644 index 00000000..4aaa7fed Binary files /dev/null and b/doc/_content/qa/_media/img_2.png differ diff --git a/doc/_content/qa/_media/img_3.png b/doc/_content/qa/_media/img_3.png new file mode 100644 index 00000000..27f8a96a Binary files /dev/null and b/doc/_content/qa/_media/img_3.png differ diff --git a/doc/_content/qa/_media/img_4.png b/doc/_content/qa/_media/img_4.png new file mode 100644 index 00000000..aa3b88ee Binary files /dev/null and b/doc/_content/qa/_media/img_4.png differ diff --git a/doc/_content/qa/_media/img_5.png b/doc/_content/qa/_media/img_5.png new file mode 100644 index 00000000..76e6fafd Binary files /dev/null and b/doc/_content/qa/_media/img_5.png differ diff --git a/doc/_content/qa/bug.md b/doc/_content/qa/bug.md new file mode 100644 index 00000000..f452161a --- /dev/null +++ b/doc/_content/qa/bug.md @@ -0,0 +1,19 @@ + +# 反馈bug +代码是在不断的完善的,不断修改会修复旧的问题也有可能引入新的问题,所以遇到BUG是很正常的一件事。所以遇到问题不要烦燥,咱们就事论事就好了。 +## 如何反馈 +1. 更新代码,很可能你遇到问题别人已经更早的遇到了,或者是作者自己发现了,已经解决了,所以你可以更新代码再次进行测试; +2. 可以在github提ISSUE,我几乎每天都会去看issue,你的问题我会尽快给予答复; +3. 你可以来我的QQ群里,询问群友看看是否遇到了同样的问题; +4. 你可以私聊我的QQ,如果我有时间我会给你答复,但是除非你有明确的复现步骤或者修复方案,否则你可能等不到我的答复。 + +## 如何快速解决BUG +目前解决BUG有三种方式: +1. 作者验证以及修复; +2. 热心开发者提来的PR; +3. 使用运维手段屏蔽BUG的影响。 + +- 对于第一种:详细的复现步骤,完整的抓包文件,有条理的错误分析都可以帮助作者复现问题,进而解决问题。解决问题往往不是最难的,复现才是。 +- 对于第二种:如果你是开发者,你已经发现了造成BUG的原因以及知道如何正确的修复,那么我很希望你PR,SRS的大佬经常说的,开源不是一个人的事。所以你的参与就是最大的鼓励。 +- 对于第三种:如果你有一个有经验的运维伙伴,那么部分问题是可以通过运维的手段暂时屏蔽的,在等待修复的这段时间了以保证项目的运行。 + diff --git a/doc/_content/qa/development.md b/doc/_content/qa/development.md new file mode 100644 index 00000000..fec7b70c --- /dev/null +++ b/doc/_content/qa/development.md @@ -0,0 +1,15 @@ + +# 参与到开发中来 +非常欢迎有兴趣的小伙伴一起来维护这个项目 +## 与开发有关的信息 +- 开发语言:后端java + 前端vue; +- jdk版本: 1.8; +- 作者自用开发ide: jetbrains intellij idea; +- nodejs/npm版本:v10.19.0/6.14.4; +- 后端使用Spring boot框架开发; +- 项目大量使用了异步操作; +- 跟代码学流程需要参考28181文档,只看代码你会很懵的; +- 必须学会[抓包](_content/skill/tcpdump.md),这是必须的 + +## 提交代码 +大家可以通过fork项目的方式提交自己的代码,然后提交PR,我来合并到主线。提交代码的过程中我们需要遵循“**阿里编码规约**”,现有代码也有很多代码没有做到,但是我们在朝这个方向努力。 \ No newline at end of file diff --git a/doc/_content/qa/img.png b/doc/_content/qa/img.png new file mode 100644 index 00000000..d6c29d71 Binary files /dev/null and b/doc/_content/qa/img.png differ diff --git a/doc/_content/qa/play_error.md b/doc/_content/qa/play_error.md new file mode 100644 index 00000000..c8e9c02b --- /dev/null +++ b/doc/_content/qa/play_error.md @@ -0,0 +1,57 @@ + +# 点播错误 +排查点播错误你首先要清楚[点播的基本流程](_content/theory/play.md),一般的流程如下: +```plantuml +@startuml +"WEB用户" -> "WVP-PRO": 1. 发起点播请求 +"设备" <- "WVP-PRO": 2. Invite(携带SDP消息体) +"设备" --> "WVP-PRO": 3. 200OK(携带SDP消息体) +"设备" <-- "WVP-PRO": 4. Ack +"设备" -> "ZLMediaKit": 5. 发送实时流 +"WVP-PRO" <- "ZLMediaKit": 6. 流改变事件 +"WEB用户" <-- "WVP-PRO": 7. 回复流播放地址(携带流地址) +"WVP-PRO" <- "ZLMediaKit": 8. 无人观看事件 +"设备" <- "WVP-PRO": 9 Bye消息 +"设备" --> "WVP-PRO": 10 200OK +@enduml +``` +针对几种常见的错误,我们来分析一下,也方便大家对号入座解决常见的问题 +## 点播收到错误码 +这个错误一般表现为点击"播放"按钮后很快得到一个错误。 +1. **400错误码** +出现400错误玛时一般是这样的流程是这样的 +```plantuml +@startuml +"WEB用户" -> "WVP-PRO": 1. 发起点播请求 +"设备" <- "WVP-PRO": 2. Invite(携带SDP消息体) +"设备" --> "WVP-PRO": 3. 400错误 +@enduml +``` +此时通常是设备认为WVP发送了错误的消息给它,它认为消息不全或者错误所以直接返回400错误,此时我们需要[抓包](_content/skill/tcpdump.md)来分析是否缺失了内容,也可以直接联系对方询问为什么返回了400。 +WVP不能保证兼容所有的设备,有些实现不规范的设备可能在对接时就会出现上述问题,你可以联系作者帮忙对接。 +2. **500错误码** +500或者大于500小于600的错误码一般多是设备内部出了问题,解决方式有两个,第一种直接联系设备/平台客服寻求解决;第二种,如果你有确定可以对接这个设备的平台那么可以把对接这个平台的抓包和对接wvp的抓包同时发送给我,我来尝试解决。 + + +## 点播超时 +点播超时的情况大致分为两种:点播超时和收流超时 +1. **点播超时** +点播超时错误一般为信令的超时,比如长时间为收到对方的回复,可能出现在流程中 “3. 200OK(携带SDP消息体)”这个位置,即我们发送点播消息,但是设备没有回复,可能的原因: +> 1. 设备内部错误,未能回复消息 +> 2. 网络原因消息未到到达设备 + +大部分时候是原因2,所以遇到这个错误我们首先要排查我们我的网路,如果你是公网部署,那么也可能时心跳周期太长,导致的路由NAT失效,WVP的消息无法通道原来的IP端口号发送给设备。 + +2. **收流超时** +收流超时可能发生在流程中的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日志是否有流注册; +- 抓包查看流的信息,看看流是否正常发送,甚至可以导出发送原始流,用vlc播放,看看是否可以播放。 diff --git a/doc/_content/qa/regiser_error.md b/doc/_content/qa/regiser_error.md new file mode 100644 index 00000000..d18459c5 --- /dev/null +++ b/doc/_content/qa/regiser_error.md @@ -0,0 +1,8 @@ + +# 设备注册不上来的解决办法 +一般的原因有两个 +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 new file mode 100644 index 00000000..1dd533f2 --- /dev/null +++ b/doc/_content/qa/start_error.md @@ -0,0 +1,24 @@ + +# 启动时报错 +启动时的报错大部分时候是因为你的配置有问题,比如mysql没连接上,redis没连接上,18080/15060端口占用了,这些都会导致启动是报错,修改配置配置之后都可以解决; +下面我整理的一些常见的错误,大家可以先对号入座的简单排查下。 +> **常见错误** + +![_media/img.png](_media/img.png) +**错误原因:** redis配置错误,可能原因: redis未启动/ip错误/端口错误/网络不通 +--- +![_media/img_1.png](_media/img_1.png) +**错误原因:** redis配置错误,可能原因: 密码错误 +--- +![_media/img_2.png](_media/img_2.png) +**错误原因:** mysql配置错误,可能原因: mysql未启动/ip错误/端口错误/网络不通 +--- +![_media/img_3.png](_media/img_3.png) +**错误原因:** mysql配置错误,可能原因: 用户名/密码错误 +--- +![_media/img_4.png](_media/img_4.png) +**错误原因:** SIP配置错误,可能原因: SIP端口被占用 +--- +![_media/img_5.png](_media/img_5.png) +**错误原因:** WVP Tomcat端口配置错误,可能原因: server.port端口被占用 +--- \ No newline at end of file diff --git a/doc/_content/skill/_media/img.png b/doc/_content/skill/_media/img.png new file mode 100644 index 00000000..a9bc95fc Binary files /dev/null and b/doc/_content/skill/_media/img.png differ diff --git a/doc/_content/skill/_media/img_1.png b/doc/_content/skill/_media/img_1.png new file mode 100644 index 00000000..e08e4e1e Binary files /dev/null and b/doc/_content/skill/_media/img_1.png differ diff --git a/doc/_content/skill/_media/img_2.png b/doc/_content/skill/_media/img_2.png new file mode 100644 index 00000000..2af0ecc6 Binary files /dev/null and b/doc/_content/skill/_media/img_2.png differ diff --git a/doc/_content/skill/tcpdump.md b/doc/_content/skill/tcpdump.md new file mode 100644 index 00000000..7dd270de --- /dev/null +++ b/doc/_content/skill/tcpdump.md @@ -0,0 +1,62 @@ + +# 抓包 +如果说对于网络编程,有什么工具是必会的,我觉得抓包肯定是其中之一了。作为GB/T 28181调试过程中最重要的手段,我觉得如果你真对他有兴趣,或者系统遇到问题可以最快的得到解决,那么抓包你就一定要学会了。 + +## 抓包工具的选择 +### 1. Wireshark +在具备图形界面的系统上,比如windows,linux发行版ubuntu,opensuse等,我一般直接使用Wireshark直接进行抓包,也方便进行内容的查看。 +### 2. Tcpdump +在使用命令行的系统,比如linux服务器,我一般使用Tcpdump进行抓包,无需额外安装,系统一般自带,抓包的到的文件,可以使用Wireshark打开,在图形界面下方便查看内容。 + +## 工具安装 +Wireshark的安装很简单,根据提示一步步点击就好了,在linux需要解决权限的问题,如果和我一样使用图形界面的linux发行版的话,可以参看如下步骤; windows的小伙伴直接略过即可 +```shell +# 1. 添加wireshark用户组 +sudo groupadd wireshark +# 2. 将dumpcap更改为wireshark用户组 +sudo chgrp wireshark /usr/bin/dumpcap +# 3. 让wireshark用户组有root权限使用dumpcap +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` +详细的过滤规则可以自行百度,我可以提供一些常用的给大家参考 +![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点击红色的停止即可,需要保存文件可以点击`文件->导出特定分组`导出过滤后的数据,也可以直接`文件->另存为`保存未过滤的数据。 +### 使用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查看了 +更多的操作可以参考: [https://www.cnblogs.com/jiujuan/p/9017495.html](https://www.cnblogs.com/jiujuan/p/9017495.html) diff --git a/doc/_content/theory/_media/img.png b/doc/_content/theory/_media/img.png new file mode 100644 index 00000000..ecf62e9f Binary files /dev/null and b/doc/_content/theory/_media/img.png differ diff --git a/doc/_content/theory/_media/img_1.png b/doc/_content/theory/_media/img_1.png new file mode 100644 index 00000000..2dc8cc8a Binary files /dev/null and b/doc/_content/theory/_media/img_1.png differ diff --git a/doc/_content/theory/_media/img_2.png b/doc/_content/theory/_media/img_2.png new file mode 100644 index 00000000..7e2ddde4 Binary files /dev/null and b/doc/_content/theory/_media/img_2.png differ diff --git a/doc/_content/theory/_media/img_3.png b/doc/_content/theory/_media/img_3.png new file mode 100644 index 00000000..5fc5ef41 Binary files /dev/null and b/doc/_content/theory/_media/img_3.png differ diff --git a/doc/_content/theory/_media/img_4.png b/doc/_content/theory/_media/img_4.png new file mode 100644 index 00000000..d5df7ceb Binary files /dev/null and b/doc/_content/theory/_media/img_4.png differ diff --git a/doc/_content/theory/_media/img_5.png b/doc/_content/theory/_media/img_5.png new file mode 100644 index 00000000..47daffcd Binary files /dev/null and b/doc/_content/theory/_media/img_5.png differ diff --git a/doc/_content/theory/_media/img_6.png b/doc/_content/theory/_media/img_6.png new file mode 100644 index 00000000..6c67ef47 Binary files /dev/null and b/doc/_content/theory/_media/img_6.png differ diff --git a/doc/_content/theory/_media/img_7.png b/doc/_content/theory/_media/img_7.png new file mode 100644 index 00000000..fc204aaa Binary files /dev/null and b/doc/_content/theory/_media/img_7.png differ diff --git a/doc/_content/theory/_media/img_8.png b/doc/_content/theory/_media/img_8.png new file mode 100644 index 00000000..9b436417 Binary files /dev/null and b/doc/_content/theory/_media/img_8.png differ diff --git a/doc/_content/theory/_media/img_9.png b/doc/_content/theory/_media/img_9.png new file mode 100644 index 00000000..c3aa1ff6 Binary files /dev/null and b/doc/_content/theory/_media/img_9.png differ diff --git a/doc/_content/theory/channel_tree.md b/doc/_content/theory/channel_tree.md new file mode 100644 index 00000000..c4c1b09e --- /dev/null +++ b/doc/_content/theory/channel_tree.md @@ -0,0 +1,14 @@ + + +# 通道的树形结构 + +国标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 new file mode 100644 index 00000000..d5a892e2 --- /dev/null +++ b/doc/_content/theory/code.md @@ -0,0 +1,25 @@ + + +# 统一编码规则 +## D.1 编码规则 A +>  编码规则 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) + + +## D.2 编码规则 B +>  编码规则 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。 +![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/img.png b/doc/_content/theory/img.png new file mode 100644 index 00000000..9b436417 Binary files /dev/null and b/doc/_content/theory/img.png differ diff --git a/doc/_content/theory/play.md b/doc/_content/theory/play.md new file mode 100644 index 00000000..fbfdcc66 --- /dev/null +++ b/doc/_content/theory/play.md @@ -0,0 +1,33 @@ + + +# 点播流程 +> 以下为WVP-PRO点播流程。点播成功前的任何一个环节出现问题都可能出现点播超时,这也是排查点播超时的依据。 + +```plantuml +@startuml +"WEB用户" -> "WVP-PRO": 1. 发起点播请求 +"设备" <- "WVP-PRO": 2. Invite(携带SDP消息体) +"设备" --> "WVP-PRO": 3. 200OK(携带SDP消息体) +"设备" <-- "WVP-PRO": 4. Ack +"设备" -> "ZLMediaKit": 5. 发送实时流 +"WVP-PRO" <- "ZLMediaKit": 6. 流改变事件 +"WEB用户" <-- "WVP-PRO": 7. 回复流播放地址(携带流地址) +"WVP-PRO" <- "ZLMediaKit": 8. 无人观看事件 +"设备" <- "WVP-PRO": 9 Bye消息 +"设备" --> "WVP-PRO": 10 200OK +@enduml +``` + + +## 注册流程描述如下: +1. 用户从网页或调用接口发起点播请求; +2. WVP-PRO向摄像机发送Invite消息,消息头域中携带 Subject字段,表明点播的视频源ID、发送方媒体流序列号、ZLMediaKit接收流使用的IP、端口号、 + 接收端媒体流序列号等参数,SDP消息体中 s字段为“Play”代表实时点播,y字段描述SSRC值,f字段描述媒体参数。 +3. 摄像机向WVP-PRO回复200OK,消息体中描述了媒体流发送者发送媒体流的IP、端口、媒体格式、SSRC字段等内容。 +4. WVP-PRO向设备回复Ack, 会话建立成功。 +5. 设备向ZLMediaKit发送实时流。 +6. ZLMediaKit向WVP-PRO发送流改变事件。 +7. WVP-PRO向WEB用户回复播放地址。 +8. ZLMediaKit向WVP发送流无人观看事件。 +9. WVP-PRO向设备回复Bye, 结束会话。 +10. 设备回复200OK,会话结束成功。 diff --git a/doc/_content/theory/register.md b/doc/_content/theory/register.md new file mode 100644 index 00000000..fad9589f --- /dev/null +++ b/doc/_content/theory/register.md @@ -0,0 +1,21 @@ + + +# 注册流程 +WVP-PRO目前仅支持国标中描述的基本注册流程,也是最常用的, +> 基本注册即采用IETFRFC3261规定的基于数字摘要的挑战应答式安全技术进行注册. + +```plantuml +@startuml +"设备" -> "WVP-PRO": 1. Register +"设备" <-- "WVP-PRO": 2. 401 Unauthorized +"设备" -> "WVP-PRO": 3. Register +"设备" <-- "WVP-PRO": 4. 200 OK +@enduml +``` + + +> 注册流程描述如下: +> 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 new file mode 100644 index 00000000..eb3524f9 --- /dev/null +++ b/doc/_coverpage.md @@ -0,0 +1,17 @@ + +![logo](_media/logo-mini.png) + +# WVP-PRO 2.0 + +> 开箱即用的28181协议视频平台。 + +- 基于GB/T28181-2016标准信令实现,兼容GB/T28181-2011。 +- 自带完整前端页面,开箱即用。 +- 完全开源,且使用MIT许可协议。可以在保留版权信息的基础上商用。 + +[GitHub](https://github.com/648540858/wvp-GB28181-pro) +[Gitee](https://gitee.com/pan648540858/wvp-GB28181-pro) + + + +[//]: # ([comment]: <> (![color](#f0f0f0))) diff --git a/doc/_media/2.png b/doc/_media/2.png new file mode 100644 index 00000000..dd982e1e Binary files /dev/null and b/doc/_media/2.png differ diff --git a/doc/_media/3-1.png b/doc/_media/3-1.png new file mode 100644 index 00000000..a52620f2 Binary files /dev/null and b/doc/_media/3-1.png differ diff --git a/doc/_media/3-2.png b/doc/_media/3-2.png new file mode 100644 index 00000000..bef780eb Binary files /dev/null and b/doc/_media/3-2.png differ diff --git a/doc/_media/3-3.png b/doc/_media/3-3.png new file mode 100644 index 00000000..3943a527 Binary files /dev/null and b/doc/_media/3-3.png differ diff --git a/doc/_media/3.png b/doc/_media/3.png new file mode 100644 index 00000000..913d2949 Binary files /dev/null and b/doc/_media/3.png differ diff --git a/doc/_media/favicon.ico b/doc/_media/favicon.ico new file mode 100644 index 00000000..bc5f8e66 Binary files /dev/null and b/doc/_media/favicon.ico differ diff --git a/doc/_media/index.png b/doc/_media/index.png new file mode 100644 index 00000000..15200e69 Binary files /dev/null and b/doc/_media/index.png differ diff --git a/doc/_media/logo-mini.png b/doc/_media/logo-mini.png new file mode 100644 index 00000000..cc8078db Binary files /dev/null and b/doc/_media/logo-mini.png differ diff --git a/doc/_media/logo.png b/doc/_media/logo.png new file mode 100644 index 00000000..c5da2d4b Binary files /dev/null and b/doc/_media/logo.png differ diff --git a/doc/_media/weixin.jpg b/doc/_media/weixin.jpg new file mode 100644 index 00000000..eda1260e Binary files /dev/null and b/doc/_media/weixin.jpg differ diff --git a/doc/_media/zhifubao.jpg b/doc/_media/zhifubao.jpg new file mode 100644 index 00000000..973996b7 Binary files /dev/null and b/doc/_media/zhifubao.jpg differ diff --git a/doc/_navbar.md b/doc/_navbar.md new file mode 100644 index 00000000..22ac93ae --- /dev/null +++ b/doc/_navbar.md @@ -0,0 +1 @@ + diff --git a/doc/_sidebar.md b/doc/_sidebar.md new file mode 100644 index 00000000..3b10bae8 --- /dev/null +++ b/doc/_sidebar.md @@ -0,0 +1,32 @@ + + +* **编译与部署** + * [编译](_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/theory/code.md) + * [树形结构](_content/theory/channel_tree.md) + * [注册流程](_content/theory/register.md) + * [点播流程](_content/theory/play.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) +* [**免责声明**](_content/disclaimers.md) +* [**关于本文档**](_content/about_doc.md) diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 00000000..1048d929 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,59 @@ + + + + + WVP-PRO文档 + + + + + + + + + +
加载中
+ + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 364e6b9e..c0c99004 100644 --- a/pom.xml +++ b/pom.xml @@ -6,12 +6,12 @@ org.springframework.boot spring-boot-starter-parent - 2.3.5.RELEASE + 2.7.2 com.genersoft wvp-pro - 2.0 + 2.6.7 web video platform 国标28181视频平台 @@ -47,7 +47,6 @@ UTF-8 MMddHHmm 3.1.1 - 3.1.0 ${project.build.directory}/generated-snippets @@ -74,60 +73,52 @@ org.mybatis.spring.boot mybatis-spring-boot-starter - 2.1.4 + 2.2.2 + + + com.zaxxer + HikariCP + + org.springframework.boot spring-boot-starter-security - - redis.clients - jedis - ${jedis-version} - - com.alibaba - druid - 1.2.3 + druid-spring-boot-starter + 1.2.11 mysql mysql-connector-java - 8.0.22 - - - - - org.xerial - sqlite-jdbc - 3.32.3.2 + 8.0.30 com.github.pagehelper pagehelper-spring-boot-starter - 1.4.1 + 1.4.3 - - io.springfox - springfox-boot-starter - 3.0.0 - - - com.github.xiaoymin - knife4j-spring-boot-starter - 3.0.2 + org.springdoc + springdoc-openapi-ui + 1.6.10 + + com.github.xiaoymin + knife4j-springdoc-ui + 3.0.3 + @@ -148,10 +139,11 @@ 1.3.0-91 + - log4j - log4j - 1.2.17 + org.slf4j + log4j-over-slf4j + 1.7.36 @@ -161,32 +153,39 @@ 2.1.3 - + - com.alibaba - fastjson - 1.2.73 + com.alibaba.fastjson2 + fastjson2 + 2.0.17 - - - com.google.guava - guava - 30.0-jre + com.alibaba.fastjson2 + fastjson2-extension + 2.0.17 com.squareup.okhttp3 okhttp - 4.9.0 + 4.10.0 + + + com.squareup.okhttp3 + logging-interceptor + 4.10.0 + + + + - com.burgstaller + io.github.rburgst okhttp-digest - 2.1 + 2.7 @@ -200,7 +199,21 @@ org.mitre.dsmiley.httpproxy smiley-http-proxy-servlet - 1.12 + 1.12.1 + + + + + com.alibaba + easyexcel + 3.1.1 + + + + + com.github.oshi + oshi-core + 6.2.2 @@ -216,6 +229,14 @@ + + + com.google.guava + guava + 31.1-jre + + + org.springframework.boot spring-boot-starter-test @@ -224,13 +245,14 @@ - + ${project.artifactId}-${project.version}-${maven.build.timestamp} org.springframework.boot spring-boot-maven-plugin + 2.3.5.RELEASE true @@ -238,6 +260,7 @@ org.apache.maven.plugins maven-compiler-plugin + 3.8.1 1.8 1.8 @@ -247,16 +270,34 @@ pl.project13.maven git-commit-id-plugin + 3.0.1 + + true + false + yyyyMMdd + org.apache.maven.plugins maven-surefire-plugin + 2.22.2 true + + + src/main/resources + + + src/main/java + + **/*.xml + + + diff --git a/sql/clean.sql b/sql/clean.sql new file mode 100644 index 00000000..b8343912 --- /dev/null +++ b/sql/clean.sql @@ -0,0 +1,13 @@ +delete from device; +delete from device_alarm; +delete from device_channel; +delete from device_mobile_position; +delete from gb_stream; +delete from log; +delete from media_server; +delete from parent_platform; +delete from platform_catalog; +delete from platform_gb_channel; +delete from platform_gb_stream; +delete from stream_proxy; +delete from stream_push; \ No newline at end of file diff --git a/sql/mysql.sql b/sql/mysql.sql index 50b70bc6..7a15f905 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -1,256 +1,577 @@ --- auto-generated definition +-- MySQL dump 10.13 Distrib 8.0.31, for Linux (x86_64) +-- +-- Host: 127.0.0.1 Database: wvp +-- ------------------------------------------------------ +-- Server version 8.0.30 +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -CREATE DATABASE `wvp` /*!40100 DEFAULT CHARACTER SET utf8mb3 COLLATE utf8mb3_bin */; +-- +-- Table structure for table `device` +-- -use wvp; +DROP TABLE IF EXISTS `device`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `device` ( + `id` int NOT NULL AUTO_INCREMENT, + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `manufacturer` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `firmware` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `streamMode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `port` int DEFAULT NULL, + `expires` int DEFAULT NULL, + `keepaliveIntervalTime` int DEFAULT NULL, + `subscribeCycleForCatalog` int DEFAULT NULL, + `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `charset` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `subscribeCycleForMobilePosition` int DEFAULT NULL, + `mobilePositionSubmissionInterval` int DEFAULT '5', + `subscribeCycleForAlarm` int DEFAULT NULL, + `ssrcCheck` int DEFAULT '0', + `geoCoordSys` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `treeType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `custom_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `sdpIp` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + `localIp` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `device_deviceId_uindex` (`deviceId`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; -create table device -( - deviceId varchar(50) not null - primary key, - name varchar(255) null, - manufacturer varchar(255) null, - model varchar(255) null, - firmware varchar(255) null, - transport varchar(50) null, - streamMode varchar(50) null, - online varchar(50) null, - registerTime varchar(50) null, - keepaliveTime varchar(50) null, - ip varchar(50) not null, - createTime varchar(50) not null, - updateTime varchar(50) not null, - port int not null, - expires int not null, - subscribeCycleForCatalog int not null, - hostAddress varchar(50) not null, - charset varchar(50) not null -); +-- +-- Dumping data for table `device` +-- -create table device_channel -( - channelId varchar(50) not null, - name varchar(255) null, - manufacture varchar(50) null, - model varchar(50) null, - owner varchar(50) null, - civilCode varchar(50) null, - block varchar(50) null, - address varchar(50) null, - parentId varchar(50) null, - safetyWay int null, - registerWay int null, - certNum varchar(50) null, - certifiable int null, - errCode int null, - endTime varchar(50) null, - secrecy varchar(50) null, - ipAddress varchar(50) null, - port int null, - password varchar(255) null, - PTZType int null, - status int null, - longitude double null, - latitude double null, - streamId varchar(50) null, - deviceId varchar(50) not null, - parental varchar(50) null, - hasAudio bit null, - createTime varchar(50) not null, - updateTime varchar(50) not null, - primary key (channelId, deviceId) -); +LOCK TABLES `device` WRITE; +/*!40000 ALTER TABLE `device` DISABLE KEYS */; +/*!40000 ALTER TABLE `device` ENABLE KEYS */; +UNLOCK TABLES; -create table device_alarm -( - id int auto_increment - primary key, - deviceId varchar(50) not null, - channelId varchar(50) not null, - alarmPriority varchar(50) not null, - alarmMethod varchar(50), - alarmTime varchar(50) not null, - alarmDescription varchar(255), - longitude double null, - latitude double null, - alarmType varchar(50) -); +-- +-- Table structure for table `device_alarm` +-- -create table log -( - id int auto_increment - primary key, - name varchar(50) not null, - type varchar(50) not null, - uri varchar(200) not null, - address varchar(50) not null, - result varchar(50) not null, - timing bigint not null, - username varchar(50) not null, - createTime varchar(50) not null -); +DROP TABLE IF EXISTS `device_alarm`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `device_alarm` ( + `id` int NOT NULL AUTO_INCREMENT, + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `alarmPriority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `alarmMethod` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `alarmTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `alarmDescription` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `longitude` double DEFAULT NULL, + `latitude` double DEFAULT NULL, + `alarmType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; -create table device_mobile_position -( - deviceId varchar(50) not null, - channelId varchar(50) not null, - deviceName varchar(255) null, - time varchar(50) not null, - longitude double not null, - latitude double not null, - altitude double null, - speed double null, - direction double null, - reportSource varchar(50) null, - geodeticSystem varchar(50) null, - cnLng varchar(50) null, - cnLat varchar(50) null, - primary key (deviceId, time) -); +-- +-- Dumping data for table `device_alarm` +-- -create table gb_stream -( - app varchar(255) not null, - stream varchar(255) not null, - gbId varchar(50) not null, - name varchar(255) null, - longitude double null, - latitude double null, - streamType varchar(50) null, - mediaServerId varchar(50) null, - status int null, - primary key (app, stream, gbId) -); +LOCK TABLES `device_alarm` WRITE; +/*!40000 ALTER TABLE `device_alarm` DISABLE KEYS */; +/*!40000 ALTER TABLE `device_alarm` ENABLE KEYS */; +UNLOCK TABLES; -create table media_server -( - id varchar(255) not null - primary key, - ip varchar(50) not null, - hookIp varchar(50) not null, - sdpIp varchar(50) not null, - streamIp varchar(50) not null, - httpPort int not null, - httpSSlPort int not null, - rtmpPort int not null, - rtmpSSlPort int not null, - rtpProxyPort int not null, - rtspPort int not null, - rtspSSLPort int not null, - autoConfig int not null, - secret varchar(50) not null, - streamNoneReaderDelayMS int not null, - rtpEnable int not null, - rtpPortRange varchar(50) not null, - sendRtpPortRange varchar(50) not null, - recordAssistPort int not null, - defaultServer int not null, - createTime varchar(50) not null, - updateTime varchar(50) not null, - hookAliveInterval int not null, - constraint media_server_i - unique (ip, httpPort) -); +-- +-- Table structure for table `device_channel` +-- -create table parent_platform -( - id int auto_increment, - enable int null, - name varchar(255) null, - serverGBId varchar(50) not null, - serverGBDomain varchar(50) null, - serverIP varchar(50) null, - serverPort int null, - deviceGBId varchar(50) not null, - deviceIp varchar(50) null, - devicePort varchar(50) null, - username varchar(255) null, - password varchar(50) null, - expires varchar(50) null, - keepTimeout varchar(50) null, - transport varchar(50) null, - characterSet varchar(50) null, - ptz int null, - rtcp int null, - status bit null, - shareAllLiveStream int null, - primary key (id, serverGBId) -); +DROP TABLE IF EXISTS `device_channel`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `device_channel` ( + `id` int NOT NULL AUTO_INCREMENT, + `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `manufacture` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `model` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `owner` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `civilCode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `block` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `safetyWay` int DEFAULT NULL, + `registerWay` int DEFAULT NULL, + `certNum` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `certifiable` int DEFAULT NULL, + `errCode` int DEFAULT NULL, + `endTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `secrecy` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `ipAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `port` int DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `PTZType` int DEFAULT NULL, + `status` int DEFAULT NULL, + `longitude` double DEFAULT NULL, + `latitude` double DEFAULT NULL, + `streamId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `parental` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `hasAudio` bit(1) DEFAULT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `subCount` int DEFAULT '0', + `longitudeGcj02` double DEFAULT NULL, + `latitudeGcj02` double DEFAULT NULL, + `longitudeWgs84` double DEFAULT NULL, + `latitudeWgs84` double DEFAULT NULL, + `businessGroupId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `gpsTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `device_channel_id_uindex` (`id`), + UNIQUE KEY `device_channel_pk` (`channelId`,`deviceId`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; -create table platform_gb_channel -( - channelId varchar(50) not null, - deviceId varchar(50) not null, - platformId varchar(50) not null, - deviceAndChannelId varchar(50) not null, - primary key (deviceAndChannelId, platformId) -); +-- +-- Dumping data for table `device_channel` +-- -create table platform_gb_stream -( - platformId varchar(50) not null, - app varchar(255) not null, - stream varchar(255) not null, - primary key (platformId, app, stream) -); +LOCK TABLES `device_channel` WRITE; +/*!40000 ALTER TABLE `device_channel` DISABLE KEYS */; +/*!40000 ALTER TABLE `device_channel` ENABLE KEYS */; +UNLOCK TABLES; -create table stream_proxy -( - type varchar(50) not null, - app varchar(255) not null, - stream varchar(255) not null, - url varchar(255) null, - src_url varchar(255) null, - dst_url varchar(255) null, - timeout_ms int null, - ffmpeg_cmd_key varchar(255) null, - rtp_type varchar(50) null, - mediaServerId varchar(50) null, - enable_hls bit null, - enable_mp4 bit null, - enable bit not null, - enable_remove_none_reader bit not null, - createTime varchar(50) not null, - primary key (app, stream) -); +-- +-- Table structure for table `device_mobile_position` +-- -create table stream_push -( - app varchar(255) not null, - stream varchar(255) not null, - totalReaderCount varchar(50) null, - originType int null, - originTypeStr varchar(50) null, - createStamp int null, - aliveSecond int null, - mediaServerId varchar(50) null, - primary key (app, stream) -); +DROP TABLE IF EXISTS `device_mobile_position`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `device_mobile_position` ( + `id` int NOT NULL AUTO_INCREMENT, + `deviceId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `channelId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `deviceName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `time` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `longitude` double NOT NULL, + `latitude` double NOT NULL, + `altitude` double DEFAULT NULL, + `speed` double DEFAULT NULL, + `direction` double DEFAULT NULL, + `reportSource` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `longitudeGcj02` double DEFAULT NULL, + `latitudeGcj02` double DEFAULT NULL, + `longitudeWgs84` double DEFAULT NULL, + `latitudeWgs84` double DEFAULT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; -create table user -( - id int auto_increment - primary key, - username varchar(255) not null, - password varchar(255) not null, - roleId int not null, - createTime varchar(50) not null, - updateTime varchar(50) not null -); +-- +-- Dumping data for table `device_mobile_position` +-- -create unique index user_username_uindex - on user (username); +LOCK TABLES `device_mobile_position` WRITE; +/*!40000 ALTER TABLE `device_mobile_position` DISABLE KEYS */; +/*!40000 ALTER TABLE `device_mobile_position` ENABLE KEYS */; +UNLOCK TABLES; -insert into user (username, password, roleId, createTime, updateTime) values ('admin', '21232f297a57a5a743894a0e4a801fc3', '1', '2021-04-13 14:14:57', '2021-04-13 14:14:57'); +-- +-- Table structure for table `gb_stream` +-- -create table role ( - id int auto_increment - primary key, - name TEXT NOT NULL, - authority TEXT NOT NULL, - createTime varchar(50) not null, - updateTime varchar(50) not null -); -insert into role (id, name, authority, createTime, updateTime) values ('1', 'admin', '0', '2021-04-13 14:14:57', '2021-04-13 14:14:57'); +DROP TABLE IF EXISTS `gb_stream`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `gb_stream` ( + `gbStreamId` int NOT NULL AUTO_INCREMENT, + `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `gbId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `longitude` double DEFAULT NULL, + `latitude` double DEFAULT NULL, + `streamType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`gbStreamId`) USING BTREE, + UNIQUE KEY `app` (`app`,`stream`) USING BTREE, + UNIQUE KEY `gbId` (`gbId`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Dumping data for table `gb_stream` +-- + +LOCK TABLES `gb_stream` WRITE; +/*!40000 ALTER TABLE `gb_stream` DISABLE KEYS */; +/*!40000 ALTER TABLE `gb_stream` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `log` +-- + +DROP TABLE IF EXISTS `log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `log` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `uri` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `result` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `timing` bigint NOT NULL, + `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `log` +-- + +LOCK TABLES `log` WRITE; +/*!40000 ALTER TABLE `log` DISABLE KEYS */; +/*!40000 ALTER TABLE `log` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `media_server` +-- + +DROP TABLE IF EXISTS `media_server`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `media_server` ( + `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `hookIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `sdpIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `streamIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `httpPort` int NOT NULL, + `httpSSlPort` int NOT NULL, + `rtmpPort` int NOT NULL, + `rtmpSSlPort` int NOT NULL, + `rtpProxyPort` int NOT NULL, + `rtspPort` int NOT NULL, + `rtspSSLPort` int NOT NULL, + `autoConfig` int NOT NULL, + `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `rtpEnable` int NOT NULL, + `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `recordAssistPort` int NOT NULL, + `defaultServer` int NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `hookAliveInterval` int NOT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `media_server_i` (`ip`,`httpPort`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `media_server` +-- + +LOCK TABLES `media_server` WRITE; +/*!40000 ALTER TABLE `media_server` DISABLE KEYS */; +/*!40000 ALTER TABLE `media_server` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `parent_platform` +-- + +DROP TABLE IF EXISTS `parent_platform`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `parent_platform` ( + `id` int NOT NULL AUTO_INCREMENT, + `enable` int DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `serverGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `serverGBDomain` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `serverIP` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `serverPort` int DEFAULT NULL, + `deviceGBId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `deviceIp` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `devicePort` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `expires` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `keepTimeout` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `characterSet` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `ptz` int DEFAULT NULL, + `rtcp` int DEFAULT NULL, + `status` bit(1) DEFAULT NULL, + `startOfflinePush` int DEFAULT '0', + `administrativeDivision` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `catalogGroup` int DEFAULT '1', + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `treeType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `parent_platform_id_uindex` (`id`), + UNIQUE KEY `parent_platform_pk` (`serverGBId`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `parent_platform` +-- + +LOCK TABLES `parent_platform` WRITE; +/*!40000 ALTER TABLE `parent_platform` DISABLE KEYS */; +/*!40000 ALTER TABLE `parent_platform` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `platform_catalog` +-- + +DROP TABLE IF EXISTS `platform_catalog`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `platform_catalog` ( + `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `parentId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `civilCode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `businessGroupId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `platform_catalog` +-- + +LOCK TABLES `platform_catalog` WRITE; +/*!40000 ALTER TABLE `platform_catalog` DISABLE KEYS */; +/*!40000 ALTER TABLE `platform_catalog` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `platform_gb_channel` +-- + +DROP TABLE IF EXISTS `platform_gb_channel`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `platform_gb_channel` ( + `id` int NOT NULL AUTO_INCREMENT, + `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `deviceChannelId` int NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `platform_gb_channel` +-- + +LOCK TABLES `platform_gb_channel` WRITE; +/*!40000 ALTER TABLE `platform_gb_channel` DISABLE KEYS */; +/*!40000 ALTER TABLE `platform_gb_channel` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `platform_gb_stream` +-- + +DROP TABLE IF EXISTS `platform_gb_stream`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `platform_gb_stream` ( + `id` int NOT NULL AUTO_INCREMENT, + `platformId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `gbStreamId` int NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `platform_gb_stream_pk` (`platformId`,`catalogId`,`gbStreamId`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `platform_gb_stream` +-- + +LOCK TABLES `platform_gb_stream` WRITE; +/*!40000 ALTER TABLE `platform_gb_stream` DISABLE KEYS */; +/*!40000 ALTER TABLE `platform_gb_stream` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `stream_proxy` +-- + +DROP TABLE IF EXISTS `stream_proxy`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `stream_proxy` ( + `id` int NOT NULL AUTO_INCREMENT, + `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `src_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `dst_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `timeout_ms` int DEFAULT NULL, + `ffmpeg_cmd_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `rtp_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `enable_audio` bit(1) DEFAULT NULL, + `enable_mp4` bit(1) DEFAULT NULL, + `enable` bit(1) NOT NULL, + `status` bit(1) NOT NULL, + `enable_remove_none_reader` bit(1) NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `enable_disable_none_reader` bit(1) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `stream_proxy_pk` (`app`,`stream`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `stream_proxy` +-- + +LOCK TABLES `stream_proxy` WRITE; +/*!40000 ALTER TABLE `stream_proxy` DISABLE KEYS */; +/*!40000 ALTER TABLE `stream_proxy` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `stream_push` +-- + +DROP TABLE IF EXISTS `stream_push`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `stream_push` ( + `id` int NOT NULL AUTO_INCREMENT, + `app` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `stream` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `totalReaderCount` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `originType` int DEFAULT NULL, + `originTypeStr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `aliveSecond` int DEFAULT NULL, + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `serverId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `pushTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `status` int DEFAULT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `pushIng` int DEFAULT NULL, + `self` int DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `stream_push_pk` (`app`,`stream`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `stream_push` +-- + +LOCK TABLES `stream_push` WRITE; +/*!40000 ALTER TABLE `stream_push` DISABLE KEYS */; +/*!40000 ALTER TABLE `stream_push` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user` +-- + +DROP TABLE IF EXISTS `user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user` ( + `id` int NOT NULL AUTO_INCREMENT, + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `roleId` int NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `pushKey` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `user_username_uindex` (`username`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user` +-- + +LOCK TABLES `user` WRITE; +/*!40000 ALTER TABLE `user` DISABLE KEYS */; +INSERT INTO `user` VALUES (1,'admin','21232f297a57a5a743894a0e4a801fc3',1,'2021-04-13 14:14:57','2021-04-13 14:14:57','3e80d1762a324d5b0ff636e0bd16f1e3'); +/*!40000 ALTER TABLE `user` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_role` +-- + +DROP TABLE IF EXISTS `user_role`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `user_role` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_role` +-- + +LOCK TABLES `user_role` WRITE; +/*!40000 ALTER TABLE `user_role` DISABLE KEYS */; +INSERT INTO `user_role` VALUES (1,'admin','0','2021-04-13 14:14:57','2021-04-13 14:14:57'); +/*!40000 ALTER TABLE `user_role` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2022-11-29 11:47:46 \ No newline at end of file diff --git a/sql/update.sql b/sql/update.sql new file mode 100644 index 00000000..2e5d5691 --- /dev/null +++ b/sql/update.sql @@ -0,0 +1,3 @@ +-- 2.6.6->2.6.7 +alter table device + add keepaliveIntervalTime int default null; \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java index bfe58419..af3340dd 100644 --- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java +++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java @@ -2,27 +2,39 @@ package com.genersoft.iot.vmp; import java.util.logging.LogManager; +import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport; +import com.genersoft.iot.vmp.storager.impl.RedisCatchStorageImpl; +import com.genersoft.iot.vmp.utils.GitUtil; +import com.genersoft.iot.vmp.utils.SpringBeanFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; -import springfox.documentation.oas.annotations.EnableOpenApi; /** - * + * 启动类 */ @ServletComponentScan("com.genersoft.iot.vmp.conf") @SpringBootApplication @EnableScheduling -@EnableOpenApi +@EnableDruidSupport public class VManageBootstrap extends LogManager { + + 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()); } // 项目重启 public static void restart() { diff --git a/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java b/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java index ec2a7252..ed1c2b9b 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java +++ b/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java @@ -1,5 +1,9 @@ package com.genersoft.iot.vmp.common; +/** + * 为API重命名, 方便向数据库记录数据的时候展示 + * @author lin + */ public class ApiSaveConstant { public static String getVal(String key) { @@ -35,35 +39,47 @@ public class ApiSaveConstant { return "[设备控制] 强制关键帧"; case "home_position": return "[设备控制] 看守位控制"; + default: + return ""; } - break; case "query": - if (keyItemArray.length <= 5) return null; + if (keyItemArray.length <= 5) { + return null; + } switch (keyItemArray[4]) { case "devices": - if (keyItemArray.length < 7) return null; + if (keyItemArray.length < 7) { + return null; + } switch (keyItemArray[6]) { case "sync": return "[设备查询] 同步设备通道"; case "delete": return "[设备查询] 移除设备"; + default: + return ""; } - break; case "channel": return "[设备查询] 更新通道信息"; case "transport": return "[设备查询] 修改数据流传输模式"; + default: + return ""; } - break; + default: + return ""; } + + break; case "gbStream": switch (keyItemArray[3]) { case "del": return "移除通道与国标的关联"; case "add": return "添加通道与国标的关联"; + default: + return ""; } - break; case "media": break; case "position": @@ -81,8 +97,9 @@ public class ApiSaveConstant { return "向上级平台添加国标通道"; case "del_channel_for_gb": return "从上级平台移除国标通道"; + default: + return ""; } - break; case "platform_gb_stream": break; case "play": @@ -97,32 +114,36 @@ public class ApiSaveConstant { return "结束转码"; case "broadcast": return "语音广播"; + default: + return ""; } - break; case "download": switch (keyItemArray[3]) { case "start": return "开始历史媒体下载"; case "stop": return "停止历史媒体下载"; + default: + return ""; } - break; case "playback": switch (keyItemArray[3]) { case "start": return "开始视频回放"; case "stop": return "停止视频回放"; + default: + return ""; } - break; case "ptz": switch (keyItemArray[3]) { case "control": return "云台控制"; case "front_end_command": return "通用前端控制命令"; + default: + return ""; } - break; case "gb_record": break; case "onvif": @@ -142,16 +163,18 @@ public class ApiSaveConstant { return "启用代理"; case "stop": return "停用代理"; + default: + return ""; } - break; case "push": switch (keyItemArray[3]) { case "save_to_gb": return "将推流添加到国标"; case "remove_form_gb": return "将推流移出到国标"; + default: + return ""; } - break; case "user": switch (keyItemArray[3]) { case "login": @@ -162,8 +185,11 @@ public class ApiSaveConstant { return "添加用户"; case "delete": return "删除用户"; + default: + return ""; } - break; + default: + return ""; } } return null; 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 e16c1add..8311745c 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java @@ -1,36 +1,317 @@ package com.genersoft.iot.vmp.common; -import com.alibaba.fastjson.JSONArray; +import io.swagger.v3.oas.annotations.media.Schema; -public class StreamInfo { +import java.io.Serializable; +@Schema(description = "流信息") +public class StreamInfo implements Serializable, Cloneable{ + + @Schema(description = "应用名") private String app; - private String streamId; + @Schema(description = "流ID") + private String stream; + @Schema(description = "设备编号") private String deviceID; + @Schema(description = "通道编号") private String channelId; - private String flv; - private String https_flv; - private String ws_flv; - private String wss_flv; - private String fmp4; - private String https_fmp4; - private String ws_fmp4; - private String wss_fmp4; - private String hls; - private String https_hls; - private String ws_hls; - private String wss_hls; - private String ts; - private String https_ts; - private String ws_ts; - private String wss_ts; - private String rtmp; - private String rtmps; - private String rtsp; - private String rtsps; - private String rtc; + + @Schema(description = "IP") + private String ip; + + @Schema(description = "HTTP-FLV流地址") + private StreamURL flv; + + @Schema(description = "HTTPS-FLV流地址") + private StreamURL https_flv; + @Schema(description = "Websocket-FLV流地址") + private StreamURL ws_flv; + @Schema(description = "Websockets-FLV流地址") + private StreamURL wss_flv; + @Schema(description = "HTTP-FMP4流地址") + private StreamURL fmp4; + @Schema(description = "HTTPS-FMP4流地址") + private StreamURL https_fmp4; + @Schema(description = "Websocket-FMP4流地址") + private StreamURL ws_fmp4; + @Schema(description = "Websockets-FMP4流地址") + private StreamURL wss_fmp4; + @Schema(description = "HLS流地址") + private StreamURL hls; + @Schema(description = "HTTPS-HLS流地址") + private StreamURL https_hls; + @Schema(description = "Websocket-HLS流地址") + private StreamURL ws_hls; + @Schema(description = "Websockets-HLS流地址") + private StreamURL wss_hls; + @Schema(description = "HTTP-TS流地址") + private StreamURL ts; + @Schema(description = "HTTPS-TS流地址") + private StreamURL https_ts; + @Schema(description = "Websocket-TS流地址") + private StreamURL ws_ts; + @Schema(description = "Websockets-TS流地址") + private StreamURL wss_ts; + @Schema(description = "RTMP流地址") + private StreamURL rtmp; + @Schema(description = "RTMPS流地址") + private StreamURL rtmps; + @Schema(description = "RTSP流地址") + private StreamURL rtsp; + @Schema(description = "RTSPS流地址") + private StreamURL rtsps; + @Schema(description = "RTC流地址") + private StreamURL rtc; + + @Schema(description = "RTCS流地址") + private StreamURL rtcs; + @Schema(description = "流媒体ID") private String mediaServerId; + @Schema(description = "流编码信息") private Object tracks; + @Schema(description = "开始时间") + private String startTime; + @Schema(description = "结束时间") + private String endTime; + @Schema(description = "进度(录像下载使用)") + private double progress; + + @Schema(description = "是否暂停(录像回放使用)") + private boolean pause; + + public void setFlv(StreamURL flv) { + this.flv = flv; + } + + public void setHttps_flv(StreamURL https_flv) { + this.https_flv = https_flv; + } + + public void setWs_flv(StreamURL ws_flv) { + this.ws_flv = ws_flv; + } + + public void setWss_flv(StreamURL wss_flv) { + this.wss_flv = wss_flv; + } + + public void setFmp4(StreamURL fmp4) { + this.fmp4 = fmp4; + } + + public void setHttps_fmp4(StreamURL https_fmp4) { + this.https_fmp4 = https_fmp4; + } + + public void setWs_fmp4(StreamURL ws_fmp4) { + this.ws_fmp4 = ws_fmp4; + } + + public void setWss_fmp4(StreamURL wss_fmp4) { + this.wss_fmp4 = wss_fmp4; + } + + public void setHls(StreamURL hls) { + this.hls = hls; + } + + public void setHttps_hls(StreamURL https_hls) { + this.https_hls = https_hls; + } + + public void setWs_hls(StreamURL ws_hls) { + this.ws_hls = ws_hls; + } + + public void setWss_hls(StreamURL wss_hls) { + this.wss_hls = wss_hls; + } + + public void setTs(StreamURL ts) { + this.ts = ts; + } + + public void setHttps_ts(StreamURL https_ts) { + this.https_ts = https_ts; + } + + public void setWs_ts(StreamURL ws_ts) { + this.ws_ts = ws_ts; + } + + public void setWss_ts(StreamURL wss_ts) { + this.wss_ts = wss_ts; + } + + public void setRtmp(StreamURL rtmp) { + this.rtmp = rtmp; + } + + public void setRtmps(StreamURL rtmps) { + this.rtmps = rtmps; + } + + public void setRtsp(StreamURL rtsp) { + this.rtsp = rtsp; + } + + public void setRtsps(StreamURL rtsps) { + this.rtsps = rtsps; + } + + public void setRtc(StreamURL rtc) { + this.rtc = rtc; + } + + public void setRtcs(StreamURL rtcs) { + this.rtcs = rtcs; + } + + public void setRtmp(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s/%s", app, stream, callIdParam); + if (port > 0) { + this.rtmp = new StreamURL("rtmp", host, port, file); + } + if (sslPort > 0) { + this.rtmps = new StreamURL("rtmps", host, sslPort, file); + } + } + + public void setRtsp(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s/%s", app, stream, callIdParam); + if (port > 0) { + this.rtsp = new StreamURL("rtsp", host, port, file); + } + if (sslPort > 0) { + this.rtsps = new StreamURL("rtsps", host, sslPort, file); + } + } + + public void setFlv(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s.live.flv%s", app, stream, callIdParam); + if (port > 0) { + this.flv = new StreamURL("http", host, port, file); + } + this.ws_flv = new StreamURL("ws", host, port, file); + if (sslPort > 0) { + this.https_flv = new StreamURL("https", host, sslPort, file); + this.wss_flv = new StreamURL("wss", host, sslPort, file); + } + } + + public void setFmp4(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s.live.mp4%s", app, stream, callIdParam); + if (port > 0) { + this.fmp4 = new StreamURL("http", host, port, file); + this.ws_fmp4 = new StreamURL("ws", host, port, file); + } + if (sslPort > 0) { + this.https_fmp4 = new StreamURL("https", host, sslPort, file); + this.wss_fmp4 = new StreamURL("wss", host, sslPort, file); + } + } + + public void setHls(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s/hls.m3u8%s", app, stream, callIdParam); + if (port > 0) { + this.hls = new StreamURL("http", host, port, file); + this.ws_hls = new StreamURL("ws", host, port, file); + } + if (sslPort > 0) { + this.https_hls = new StreamURL("https", host, sslPort, file); + this.wss_hls = new StreamURL("wss", host, sslPort, file); + } + } + + public void setTs(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("%s/%s.live.ts%s", app, stream, callIdParam); + + if (port > 0) { + this.ts = new StreamURL("http", host, port, file); + this.ws_ts = new StreamURL("ws", host, port, file); + } + if (sslPort > 0) { + this.https_ts = new StreamURL("https", host, sslPort, file); + this.wss_ts = new StreamURL("wss", host, sslPort, file); + } + } + + public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) { + String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam); + if (port > 0) { + this.rtc = new StreamURL("http", host, port, file); + } + if (sslPort > 0) { + this.rtcs = new StreamURL("https", host, sslPort, file); + } + } + + public void channgeStreamIp(String localAddr) { + if (this.flv != null) { + this.flv.setHost(localAddr); + } + if (this.ws_flv != null ){ + this.ws_flv.setHost(localAddr); + } + if (this.hls != null ) { + this.hls.setHost(localAddr); + } + if (this.ws_hls != null ) { + this.ws_hls.setHost(localAddr); + } + if (this.ts != null ) { + this.ts.setHost(localAddr); + } + if (this.ws_ts != null ) { + this.ws_ts.setHost(localAddr); + } + if (this.fmp4 != null ) { + this.fmp4.setHost(localAddr); + } + if (this.ws_fmp4 != null ) { + this.ws_fmp4.setHost(localAddr); + } + if (this.rtc != null ) { + this.rtc.setHost(localAddr); + } + if (this.https_flv != null) { + this.https_flv.setHost(localAddr); + } + if (this.wss_flv != null) { + this.wss_flv.setHost(localAddr); + } + if (this.https_hls != null) { + this.https_hls.setHost(localAddr); + } + if (this.wss_hls != null) { + this.wss_hls.setHost(localAddr); + } + if (this.wss_ts != null) { + this.wss_ts.setHost(localAddr); + } + if (this.https_fmp4 != null) { + this.https_fmp4.setHost(localAddr); + } + if (this.wss_fmp4 != null) { + this.wss_fmp4.setHost(localAddr); + } + if (this.rtcs != null) { + this.rtcs.setHost(localAddr); + } + if (this.rtsp != null) { + this.rtsp.setHost(localAddr); + } + if (this.rtsps != null) { + this.rtsps.setHost(localAddr); + } + if (this.rtmp != null) { + this.rtmp.setHost(localAddr); + } + if (this.rtmps != null) { + this.rtmps.setHost(localAddr); + } + } + public static class TransactionInfo{ public String callId; @@ -65,116 +346,116 @@ public class StreamInfo { this.channelId = channelId; } - public String getFlv() { + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public StreamURL getFlv() { return flv; } - public void setFlv(String flv) { - this.flv = flv; + public StreamURL getHttps_flv() { + return https_flv; } - public String getWs_flv() { + public StreamURL getWs_flv() { return ws_flv; } - public void setWs_flv(String ws_flv) { - this.ws_flv = ws_flv; + + public StreamURL getWss_flv() { + return wss_flv; } - public String getRtmp() { - return rtmp; - } - - public void setRtmp(String rtmp) { - this.rtmp = rtmp; - } - - public String getHls() { - return hls; - } - - public void setHls(String hls) { - this.hls = hls; - } - - public String getRtsp() { - return rtsp; - } - - public void setRtsp(String rtsp) { - this.rtsp = rtsp; - } - - public Object getTracks() { - return tracks; - } - - public void setTracks(Object tracks) { - this.tracks = tracks; - } - - public String getFmp4() { + public StreamURL getFmp4() { return fmp4; } - public void setFmp4(String fmp4) { - this.fmp4 = fmp4; + + + public StreamURL getHttps_fmp4() { + return https_fmp4; } - public String getWs_fmp4() { + public StreamURL getWs_fmp4() { return ws_fmp4; } - public void setWs_fmp4(String ws_fmp4) { - this.ws_fmp4 = ws_fmp4; + public StreamURL getWss_fmp4() { + return wss_fmp4; } - public String getWs_hls() { + public StreamURL getHls() { + return hls; + } + + + public StreamURL getHttps_hls() { + return https_hls; + } + + public StreamURL getWs_hls() { return ws_hls; } - public void setWs_hls(String ws_hls) { - this.ws_hls = ws_hls; + public StreamURL getWss_hls() { + return wss_hls; } - public String getTs() { + public StreamURL getTs() { return ts; } - public void setTs(String ts) { - this.ts = ts; + + public StreamURL getHttps_ts() { + return https_ts; } - public String getWs_ts() { + + public StreamURL getWs_ts() { return ws_ts; } - public void setWs_ts(String ws_ts) { - this.ws_ts = ws_ts; + + public StreamURL getWss_ts() { + return wss_ts; } - public String getStreamId() { - return streamId; + + public StreamURL getRtmp() { + return rtmp; } - public void setStreamId(String streamId) { - this.streamId = streamId; + public StreamURL getRtmps() { + return rtmps; } - public String getRtc() { + public StreamURL getRtsp() { + return rtsp; + } + + public StreamURL getRtsps() { + return rtsps; + } + + public StreamURL getRtc() { return rtc; } - public void setRtc(String rtc) { - this.rtc = rtc; - } - - public TransactionInfo getTransactionInfo() { - return transactionInfo; - } - - public void setTransactionInfo(TransactionInfo transactionInfo) { - this.transactionInfo = transactionInfo; + public StreamURL getRtcs() { + return rtcs; } public String getMediaServerId() { @@ -185,83 +466,62 @@ public class StreamInfo { this.mediaServerId = mediaServerId; } - public String getHttps_flv() { - return https_flv; + public Object getTracks() { + return tracks; } - public void setHttps_flv(String https_flv) { - this.https_flv = https_flv; + public void setTracks(Object tracks) { + this.tracks = tracks; } - public String getWss_flv() { - return wss_flv; + public String getStartTime() { + return startTime; } - public void setWss_flv(String wss_flv) { - this.wss_flv = wss_flv; + public void setStartTime(String startTime) { + this.startTime = startTime; } - public String getWss_fmp4() { - return wss_fmp4; + public String getEndTime() { + return endTime; } - public void setWss_fmp4(String wss_fmp4) { - this.wss_fmp4 = wss_fmp4; + public void setEndTime(String endTime) { + this.endTime = endTime; } - public String getWss_hls() { - return wss_hls; + public double getProgress() { + return progress; } - public void setWss_hls(String wss_hls) { - this.wss_hls = wss_hls; + public void setProgress(double progress) { + this.progress = progress; } - public String getWss_ts() { - return wss_ts; + public boolean isPause() { + return pause; } - public void setWss_ts(String wss_ts) { - this.wss_ts = wss_ts; + public void setPause(boolean pause) { + this.pause = pause; } - public String getRtmps() { - return rtmps; + public TransactionInfo getTransactionInfo() { + return transactionInfo; } - public void setRtmps(String rtmps) { - this.rtmps = rtmps; + public void setTransactionInfo(TransactionInfo transactionInfo) { + this.transactionInfo = transactionInfo; } - public String getRtsps() { - return rtsps; - } - - public void setRtsps(String rtsps) { - this.rtsps = rtsps; - } - - public String getHttps_hls() { - return https_hls; - } - - public void setHttps_hls(String https_hls) { - this.https_hls = https_hls; - } - - public String getHttps_fmp4() { - return https_fmp4; - } - - public void setHttps_fmp4(String https_fmp4) { - this.https_fmp4 = https_fmp4; - } - - public String getHttps_ts() { - return https_ts; - } - - public void setHttps_ts(String https_ts) { - this.https_ts = https_ts; + @Override + public StreamInfo clone() { + StreamInfo instance = null; + try{ + instance = (StreamInfo)super.clone(); + }catch(CloneNotSupportedException e) { + e.printStackTrace(); + } + return instance; } } diff --git a/src/main/java/com/genersoft/iot/vmp/common/StreamURL.java b/src/main/java/com/genersoft/iot/vmp/common/StreamURL.java new file mode 100644 index 00000000..eecf469f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/StreamURL.java @@ -0,0 +1,80 @@ +package com.genersoft.iot.vmp.common; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.io.Serializable; + + +@Schema(description = "流地址信息") +public class StreamURL implements Serializable { + + @Schema(description = "协议") + private String protocol; + + @Schema(description = "主机地址") + private String host; + + @Schema(description = "端口") + private int port = -1; + + @Schema(description = "定位位置") + private String file; + + @Schema(description = "拼接后的地址") + private String url; + + public StreamURL() { + } + + public StreamURL(String protocol, String host, int port, String file) { + this.protocol = protocol; + this.host = host; + this.port = port; + this.file = file; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public String getUrl() { + return this.toString(); + } + + @Override + public String toString() { + if (protocol != null && host != null && port != -1 ) { + return String.format("%s://%s:%s/%s", protocol, host, port, file); + }else { + return null; + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/common/SystemAllInfo.java b/src/main/java/com/genersoft/iot/vmp/common/SystemAllInfo.java new file mode 100644 index 00000000..48485da8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/common/SystemAllInfo.java @@ -0,0 +1,54 @@ +package com.genersoft.iot.vmp.common; + +import java.util.List; + +public class SystemAllInfo { + + private List cpu; + private List mem; + private List net; + + private long netTotal; + + private Object disk; + + public List getCpu() { + return cpu; + } + + public void setCpu(List cpu) { + this.cpu = cpu; + } + + public List getMem() { + return mem; + } + + public void setMem(List mem) { + this.mem = mem; + } + + public List getNet() { + return net; + } + + public void setNet(List net) { + this.net = net; + } + + public Object getDisk() { + return disk; + } + + public void setDisk(Object disk) { + this.disk = disk; + } + + public long getNetTotal() { + return netTotal; + } + + public void setNetTotal(long netTotal) { + this.netTotal = netTotal; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/common/VersionPo.java b/src/main/java/com/genersoft/iot/vmp/common/VersionPo.java index a7d8ce67..f2c8454c 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VersionPo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VersionPo.java @@ -1,33 +1,38 @@ package com.genersoft.iot.vmp.common; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; public class VersionPo { /** * git的全版本号 */ - @JSONField(name="GIT-Revision") + @JSONField(name="GIT_Revision") private String GIT_Revision; /** * maven版本 */ - @JSONField(name = "Create-By") + @JSONField(name = "Create_By") private String Create_By; /** * git的分支 */ - @JSONField(name = "GIT-BRANCH") + @JSONField(name = "GIT_BRANCH") private String GIT_BRANCH; /** * git的url */ - @JSONField(name = "GIT-URL") + @JSONField(name = "GIT_URL") private String GIT_URL; /** * 构建日期 */ - @JSONField(name = "BUILD-DATE") + @JSONField(name = "BUILD_DATE") private String BUILD_DATE; + /** + * 构建日期 + */ + @JSONField(name = "GIT_DATE") + private String GIT_DATE; /** * 项目名称 配合pom使用 */ @@ -36,7 +41,7 @@ public class VersionPo { /** * git局部版本号 */ - @JSONField(name = "GIT-Revision-SHORT") + @JSONField(name = "GIT_Revision_SHORT") private String GIT_Revision_SHORT; /** * 项目的版本如2.0.1.0 配合pom使用 @@ -133,4 +138,12 @@ public class VersionPo { public String getBuild_Jdk() { return Build_Jdk; } + + public String getGIT_DATE() { + return GIT_DATE; + } + + public void setGIT_DATE(String GIT_DATE) { + this.GIT_DATE = GIT_DATE; + } } 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 35025255..b92f6b16 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java @@ -14,22 +14,22 @@ public class VideoManagerConstants { public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_"; - public static final String MEDIA_SERVER_KEEPALIVE_PREFIX = "VMP_MEDIA_SERVER_KEEPALIVE_"; - public static final String MEDIA_SERVERS_ONLINE_PREFIX = "VMP_MEDIA_ONLINE_SERVERS_"; public static final String MEDIA_STREAM_PREFIX = "VMP_MEDIA_STREAM"; public static final String DEVICE_PREFIX = "VMP_DEVICE_"; + // 设备同步完成 + public static final String DEVICE_SYNC_PREFIX = "VMP_DEVICE_SYNC_"; + public static final String CACHEKEY_PREFIX = "VMP_CHANNEL_"; public static final String KEEPLIVEKEY_PREFIX = "VMP_KEEPALIVE_"; - // 此处多了一个_,暂不修改 + // TODO 此处多了一个_,暂不修改 public static final String PLAYER_PREFIX = "VMP_PLAYER_"; public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_"; - public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_"; public static final String PLATFORM_KEEPALIVE_PREFIX = "VMP_PLATFORM_KEEPALIVE_"; @@ -43,8 +43,6 @@ public class VideoManagerConstants { public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_"; public static final String EVENT_ONLINE_REGISTER = "1"; - - public static final String EVENT_ONLINE_KEEPLIVE = "2"; public static final String EVENT_ONLINE_MESSAGE = "3"; @@ -56,11 +54,88 @@ public class VideoManagerConstants { public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_MEDIA_TRANSACTION_"; + public static final String MEDIA_STREAM_AUTHORITY = "MEDIA_STREAM_AUTHORITY_"; + public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_"; + public static final String SIP_SN_PREFIX = "VMP_SIP_SN_"; + + public static final String SIP_SUBSCRIBE_PREFIX = "VMP_SIP_SUBSCRIBE_"; + + public static final String SYSTEM_INFO_CPU_PREFIX = "VMP_SYSTEM_INFO_CPU_"; + + public static final String SYSTEM_INFO_MEM_PREFIX = "VMP_SYSTEM_INFO_MEM_"; + + public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_"; + + public static final String SYSTEM_INFO_DISK_PREFIX = "VMP_SYSTEM_INFO_DISK_"; + + public static final String REGISTER_EXPIRE_TASK_KEY_PREFIX = "VMP_device_register_expire_"; + + + + //************************** redis 消息********************************* + + /** + * 流变化的通知 + */ public static final String WVP_MSG_STREAM_CHANGE_PREFIX = "WVP_MSG_STREAM_CHANGE_"; + /** + * 接收推流设备的GPS变化通知 + */ + public static final String VM_MSG_GPS = "VM_MSG_GPS"; + + /** + * 接收推流设备的GPS变化通知 + */ + public static final String VM_MSG_PUSH_STREAM_STATUS_CHANGE = "VM_MSG_PUSH_STREAM_STATUS_CHANGE"; + /** + * 接收推流设备列表更新变化通知 + */ + public static final String VM_MSG_PUSH_STREAM_LIST_CHANGE = "VM_MSG_PUSH_STREAM_LIST_CHANGE"; + + /** + * redis 消息通知设备推流到平台 + */ + public static final String VM_MSG_STREAM_PUSH_REQUESTED = "VM_MSG_STREAM_PUSH_REQUESTED"; + + + /** + * redis 消息通知平台通知设备推流结果 + */ + public static final String VM_MSG_STREAM_PUSH_RESPONSE = "VM_MSG_STREAM_PUSH_RESPONSE"; + + /** + * redis 消息请求所有的在线通道 + */ + public static final String VM_MSG_GET_ALL_ONLINE_REQUESTED = "VM_MSG_GET_ALL_ONLINE_REQUESTED"; + + /** + * 移动位置订阅通知 + */ + public static final String VM_MSG_SUBSCRIBE_MOBILE_POSITION = "mobileposition"; + + /** + * 报警订阅的通知(收到报警向redis发出通知) + */ + public static final String VM_MSG_SUBSCRIBE_ALARM = "alarm"; + + /** + * 报警通知的发送 (收到redis发出的通知,转发给其他平台) + */ + public static final String VM_MSG_SUBSCRIBE_ALARM_RECEIVE= "alarm_receive"; + + /** + * 设备状态订阅的通知 + */ + public static final String VM_MSG_SUBSCRIBE_DEVICE_STATUS = "device"; + + //************************** 第三方 **************************************** + public static final String WVP_STREAM_GB_ID_PREFIX = "memberNo_"; + public static final String WVP_STREAM_GPS_MSG_PREFIX = "WVP_STREAM_GPS_MSG_"; + } 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 08d030a8..d1e61c0a 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java @@ -4,11 +4,13 @@ import com.genersoft.iot.vmp.common.ApiSaveConstant; 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 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.util.ObjectUtils; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.*; @@ -16,7 +18,6 @@ import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.text.SimpleDateFormat; /** * @author lin @@ -26,10 +27,9 @@ public class ApiAccessFilter extends OncePerRequestFilter { private final static Logger logger = LoggerFactory.getLogger(ApiAccessFilter.class); - private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Autowired - private UserSetup userSetup; + private UserSetting userSetting; @Autowired private ILogService logService; @@ -48,17 +48,20 @@ public class ApiAccessFilter extends OncePerRequestFilter { filterChain.doFilter(servletRequest, servletResponse); - if (uriName != null && userSetup.getLogInDatebase()) { + if (uriName != null && userSetting.getLogInDatebase()) { LogDto logDto = new LogDto(); logDto.setName(uriName); + if (ObjectUtils.isEmpty(username)) { + username = ""; + } logDto.setUsername(username); logDto.setAddress(servletRequest.getRemoteAddr()); logDto.setResult(HttpStatus.valueOf(servletResponse.getStatus()).toString()); logDto.setTiming(System.currentTimeMillis() - start); logDto.setType(servletRequest.getMethod()); logDto.setUri(servletRequest.getRequestURI()); - logDto.setCreateTime(format.format(System.currentTimeMillis())); + logDto.setCreateTime(DateUtil.getNow()); logService.add(logDto); // logger.warn("[Api Access] [{}] [{}] [{}] [{}] [{}] {}ms", // uriName, servletRequest.getMethod(), servletRequest.getRequestURI(), servletRequest.getRemoteAddr(), HttpStatus.valueOf(servletResponse.getStatus()), 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 10dfc086..041d7388 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java @@ -1,43 +1,141 @@ package com.genersoft.iot.vmp.conf; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; -import org.springframework.scheduling.support.CronTrigger; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; +import java.time.Instant; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; /** * 动态定时任务 + * @author lin */ @Component public class DynamicTask { - @Autowired + private final Logger logger = LoggerFactory.getLogger(DynamicTask.class); + private ThreadPoolTaskScheduler threadPoolTaskScheduler; - private Map> futureMap = new ConcurrentHashMap<>(); + private final Map> futureMap = new ConcurrentHashMap<>(); + private final Map runnableMap = new ConcurrentHashMap<>(); - @Bean - public ThreadPoolTaskScheduler threadPoolTaskScheduler() { - return new ThreadPoolTaskScheduler(); + @PostConstruct + public void DynamicTask() { + threadPoolTaskScheduler = new ThreadPoolTaskScheduler(); + threadPoolTaskScheduler.setPoolSize(300); + threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true); + threadPoolTaskScheduler.setAwaitTerminationSeconds(10); + threadPoolTaskScheduler.initialize(); } - public String startCron(String key, Runnable task, int cycleForCatalog) { - stopCron(key); + /** + * 循环执行的任务 + * @param key 任务ID + * @param task 任务 + * @param cycleForCatalog 间隔 毫秒 + * @return + */ + public void startCron(String key, Runnable task, int cycleForCatalog) { + ScheduledFuture future = futureMap.get(key); + if (future != null) { + if (future.isCancelled()) { + logger.debug("任务【{}】已存在但是关闭状态!!!", key); + } else { + logger.debug("任务【{}】已存在且已启动!!!", key); + return; + } + } // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔 - ScheduledFuture future = threadPoolTaskScheduler.scheduleWithFixedDelay(task, cycleForCatalog * 1000L); - futureMap.put(key, future); - return "startCron"; - } - - public void stopCron(String key) { - if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) { - futureMap.get(key).cancel(true); + future = threadPoolTaskScheduler.scheduleAtFixedRate(task, cycleForCatalog); + if (future != null){ + futureMap.put(key, future); + runnableMap.put(key, task); + logger.debug("任务【{}】启动成功!!!", key); + }else { + logger.debug("任务【{}】启动失败!!!", key); } } + /** + * 延时任务 + * @param key 任务ID + * @param task 任务 + * @param delay 延时 /毫秒 + * @return + */ + public void startDelay(String key, Runnable task, int delay) { + stop(key); + + // 获取执行的时刻 + Instant startInstant = Instant.now().plusMillis(TimeUnit.MILLISECONDS.toMillis(delay)); + + ScheduledFuture future = futureMap.get(key); + if (future != null) { + if (future.isCancelled()) { + logger.debug("任务【{}】已存在但是关闭状态!!!", key); + } else { + logger.debug("任务【{}】已存在且已启动!!!", key); + return; + } + } + // scheduleWithFixedDelay 必须等待上一个任务结束才开始计时period, cycleForCatalog表示执行的间隔 + future = threadPoolTaskScheduler.schedule(task, startInstant); + if (future != null){ + futureMap.put(key, future); + runnableMap.put(key, task); + logger.debug("任务【{}】启动成功!!!", key); + }else { + logger.debug("任务【{}】启动失败!!!", key); + } + } + + public boolean stop(String key) { + boolean result = false; + if (futureMap.get(key) != null && !futureMap.get(key).isCancelled() && !futureMap.get(key).isDone()) { + result = futureMap.get(key).cancel(false); + futureMap.remove(key); + runnableMap.remove(key); + } + return result; + } + + public boolean contains(String key) { + return futureMap.get(key) != null; + } + + public Set getAllKeys() { + return futureMap.keySet(); + } + + public Runnable get(String key) { + return runnableMap.get(key); + } + + /** + * 每五分钟检查失效的任务,并移除 + */ + @Scheduled(cron="0 0/5 * * * ?") + public void execute(){ + if (futureMap.size() > 0) { + for (String key : futureMap.keySet()) { + if (futureMap.get(key).isDone() || futureMap.get(key).isCancelled()) { + futureMap.remove(key); + runnableMap.remove(key); + } + } + } + } + + public boolean isAlive(String key) { + return futureMap.get(key) != null && !futureMap.get(key).isDone() && !futureMap.get(key).isCancelled(); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java new file mode 100644 index 00000000..728afb96 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java @@ -0,0 +1,57 @@ +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 org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 全局异常处理 + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + + private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 默认异常处理 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public WVPResult exceptionHandler(Exception e) { + logger.error("[全局异常]: ", e); + return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage()); + } + + + /** + * 自定义异常处理, 处理controller中返回的错误 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(ControllerException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity> exceptionHandler(ControllerException e) { + return new ResponseEntity<>(WVPResult.fail(e.getCode(), e.getMsg()), HttpStatus.OK); + } + + /** + * 登陆失败 + * @param e 异常 + * @return 统一返回结果 + */ + @ExceptionHandler(BadCredentialsException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseEntity> exceptionHandler(BadCredentialsException e) { + return new ResponseEntity<>(WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMessage()), 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 new file mode 100644 index 00000000..05f74816 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java @@ -0,0 +1,69 @@ +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; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +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.List; + +/** + * 全局统一返回结果 + * @author lin + */ +@RestControllerAdvice +public class GlobalResponseAdvice implements ResponseBodyAdvice { + + + @Override + public boolean supports(@NotNull MethodParameter returnType, @NotNull Class> converterType) { + return true; + } + + + @Override + public Object beforeBodyWrite(Object body, @NotNull MethodParameter returnType, @NotNull MediaType selectedContentType, @NotNull Class> selectedConverterType, @NotNull ServerHttpRequest request, @NotNull ServerHttpResponse response) { + // 排除api文档的接口,这个接口不需要统一 + String[] excludePath = {"/v3/api-docs","/api/v1","/index/hook"}; + for (String path : excludePath) { + if (request.getURI().getPath().startsWith(path)) { + return body; + } + } + + if (body instanceof WVPResult) { + return body; + } + + if (body instanceof ErrorCode) { + ErrorCode errorCode = (ErrorCode) body; + return new WVPResult<>(errorCode.getCode(), errorCode.getMsg(), null); + } + + if (body instanceof String) { + return JSON.toJSONString(WVPResult.success(body)); + } + + return WVPResult.success(body); + } + + /** + * 防止返回string时出错 + * @return + */ + @Bean + public HttpMessageConverters custHttpMessageConverter() { + return new HttpMessageConverters(new FastJsonHttpMessageConverter()); + } +} 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 2b52bcd3..fe535bfa 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java @@ -1,23 +1,31 @@ package com.genersoft.iot.vmp.conf; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.utils.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; -import org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.regex.Pattern; -import java.text.SimpleDateFormat; -import java.util.Date; @Configuration("mediaConfig") public class MediaConfig{ - @Value("${media.id:}") + private final static Logger logger = LoggerFactory.getLogger(MediaConfig.class); + + // 修改必须配置,不再支持自动获取 + @Value("${media.id}") private String id; @Value("${media.ip}") private String ip; - @Value("${media.hook-ip:${sip.ip}}") + @Value("${media.hook-ip:}") private String hookIp; @Value("${sip.ip}") @@ -59,19 +67,12 @@ public class MediaConfig{ @Value("${media.secret}") private String secret; - @Value("${media.stream-none-reader-delay-ms:18000}") - private int streamNoneReaderDelayMS = 18000; - @Value("${media.rtp.enable}") private boolean rtpEnable; @Value("${media.rtp.port-range}") private String rtpPortRange; - - @Value("${media.rtp.send-port-range}") - private String sendRtpPortRange; - @Value("${media.record-assist-port:0}") private Integer recordAssistPort = 0; @@ -84,8 +85,8 @@ public class MediaConfig{ } public String getHookIp() { - if (StringUtils.isEmpty(hookIp)){ - return sipIp; + if (ObjectUtils.isEmpty(hookIp)){ + return sipIp.split(",")[0]; }else { return hookIp; } @@ -141,10 +142,6 @@ public class MediaConfig{ return secret; } - public int getStreamNoneReaderDelayMS() { - return streamNoneReaderDelayMS; - } - public boolean isRtpEnable() { return rtpEnable; } @@ -158,15 +155,26 @@ public class MediaConfig{ } public String getSdpIp() { - if (StringUtils.isEmpty(sdpIp)){ + if (ObjectUtils.isEmpty(sdpIp)){ return ip; }else { - return sdpIp; + if (isValidIPAddress(sdpIp)) { + return sdpIp; + }else { + // 按照域名解析 + String hostAddress = null; + try { + hostAddress = InetAddress.getByName(sdpIp).getHostAddress(); + } catch (UnknownHostException e) { + logger.error("[获取SDP IP]: 域名解析失败"); + } + return hostAddress; + } } } public String getStreamIp() { - if (StringUtils.isEmpty(streamIp)){ + if (ObjectUtils.isEmpty(streamIp)){ return ip; }else { return streamIp; @@ -177,10 +185,6 @@ public class MediaConfig{ return sipDomain; } - public String getSendRtpPortRange() { - return sendRtpPortRange; - } - public MediaServerItem getMediaSerItem(){ MediaServerItem mediaServerItem = new MediaServerItem(); mediaServerItem.setId(id); @@ -198,18 +202,22 @@ public class MediaConfig{ mediaServerItem.setRtspSSLPort(rtspSSLPort); mediaServerItem.setAutoConfig(autoConfig); mediaServerItem.setSecret(secret); - mediaServerItem.setStreamNoneReaderDelayMS(streamNoneReaderDelayMS); mediaServerItem.setRtpEnable(rtpEnable); mediaServerItem.setRtpPortRange(rtpPortRange); - mediaServerItem.setSendRtpPortRange(sendRtpPortRange); mediaServerItem.setRecordAssistPort(recordAssistPort); - mediaServerItem.setHookAliveInterval(120); + mediaServerItem.setHookAliveInterval(30.00f); - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - mediaServerItem.setCreateTime(format.format(System.currentTimeMillis())); - mediaServerItem.setUpdateTime(format.format(System.currentTimeMillis())); + mediaServerItem.setCreateTime(DateUtil.getNow()); + mediaServerItem.setUpdateTime(DateUtil.getNow()); return mediaServerItem; } + private boolean isValidIPAddress(String ipAddress) { + if ((ipAddress != null) && (!ipAddress.isEmpty())) { + return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress); + } + return false; + } + } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java index cb9de7dc..56573fe5 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java @@ -1,6 +1,5 @@ package com.genersoft.iot.vmp.conf; -import com.alibaba.fastjson.JSONObject; import org.springframework.scheduling.annotation.Scheduled; /** 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 a6facd57..d0abfbf7 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java @@ -14,6 +14,7 @@ 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.util.ObjectUtils; import org.springframework.util.StringUtils; import javax.servlet.ServletException; @@ -55,7 +56,7 @@ public class ProxyServletConfig { String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString); MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI()); if (mediaInfo != null) { - if (!StringUtils.isEmpty(queryStr)) { + if (!ObjectUtils.isEmpty(queryStr)) { queryStr += "&secret=" + mediaInfo.getSecret(); }else { queryStr = "secret=" + mediaInfo.getSecret(); @@ -127,7 +128,7 @@ public class ProxyServletConfig { MediaServerItem getMediaInfoByUri(String uri){ String[] split = uri.split("/"); String mediaServerId = split[2]; - if ("default".equals(mediaServerId)) { + if ("default".equalsIgnoreCase(mediaServerId)) { return mediaServerService.getDefaultMediaServer(); }else { return mediaServerService.getOne(mediaServerId); @@ -146,7 +147,7 @@ public class ProxyServletConfig { logger.error("[ZLM服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); return url; } - if (!StringUtils.isEmpty(mediaInfo.getId())) { + if (!ObjectUtils.isEmpty(mediaInfo.getId())) { url = url.replace(mediaInfo.getId() + "/", ""); } return url.replace("default/", ""); @@ -173,7 +174,7 @@ public class ProxyServletConfig { MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI()); String remoteHost = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort()); if (mediaInfo != null) { - if (!StringUtils.isEmpty(queryStr)) { + if (!ObjectUtils.isEmpty(queryStr)) { queryStr += "&remoteHost=" + remoteHost; }else { queryStr = "remoteHost=" + remoteHost; @@ -245,7 +246,7 @@ public class ProxyServletConfig { MediaServerItem getMediaInfoByUri(String uri){ String[] split = uri.split("/"); String mediaServerId = split[2]; - if ("default".equals(mediaServerId)) { + if ("default".equalsIgnoreCase(mediaServerId)) { return mediaServerService.getDefaultMediaServer(); }else { return mediaServerService.getOne(mediaServerId); @@ -265,7 +266,7 @@ public class ProxyServletConfig { logger.error("[录像服务访问代理],错误:处理url信息时未找到流媒体信息=>{}", requestURI); return url; } - if (!StringUtils.isEmpty(mediaInfo.getId())) { + if (!ObjectUtils.isEmpty(mediaInfo.getId())) { url = url.replace(mediaInfo.getId() + "/", ""); } return url.replace("default/", ""); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java deleted file mode 100644 index dcb0e811..00000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/RedisConfig.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.genersoft.iot.vmp.conf; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.annotation.CachingConfigurerSupport; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.listener.RedisMessageListenerContainer; -import org.springframework.data.redis.serializer.StringRedisSerializer; - -import com.alibaba.fastjson.parser.ParserConfig; -import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.JedisPoolConfig; - -/** - * @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 - * @author: swwheihei - * @date: 2019年5月30日 上午10:58:25 - * - */ -@Configuration -public class RedisConfig extends CachingConfigurerSupport { - - @Value("${spring.redis.host}") - private String host; - @Value("${spring.redis.port}") - private int port; - @Value("${spring.redis.database}") - private int database; - @Value("${spring.redis.password}") - private String password; - @Value("${spring.redis.timeout}") - private int timeout; - @Value("${spring.redis.poolMaxTotal:1000}") - private int poolMaxTotal; - @Value("${spring.redis.poolMaxIdle:500}") - private int poolMaxIdle; - @Value("${spring.redis.poolMaxWait:5}") - private int poolMaxWait; - - @Bean - public JedisPool jedisPool() { - if (StringUtils.isBlank(password)) { - password = null; - } - JedisPoolConfig poolConfig = new JedisPoolConfig(); - poolConfig.setMaxIdle(poolMaxIdle); - poolConfig.setMaxTotal(poolMaxTotal); - // 秒转毫秒 - poolConfig.setMaxWaitMillis(poolMaxWait * 1000L); - JedisPool jp = new JedisPool(poolConfig, host, port, timeout * 1000, password, database); - return jp; - } - - @Bean("redisTemplate") - public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(redisConnectionFactory); - // 使用fastjson进行序列化处理,提高解析效率 - FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class); - // value值的序列化采用fastJsonRedisSerializer - template.setValueSerializer(serializer); - template.setHashValueSerializer(serializer); - // key的序列化采用StringRedisSerializer - template.setKeySerializer(new StringRedisSerializer()); - template.setHashKeySerializer(new StringRedisSerializer()); - template.setConnectionFactory(redisConnectionFactory); - // 使用fastjson时需设置此项,否则会报异常not support type - ParserConfig.getGlobalInstance().setAutoTypeSupport(true); - return template; - } - - /** - * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 - * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 - * - * @param connectionFactory - * @return - */ - @Bean - RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { - - RedisMessageListenerContainer container = new RedisMessageListenerContainer(); - container.setConnectionFactory(connectionFactory); - return container; - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java index 6fa802da..4790fa4a 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.conf; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; @Component @ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true) @@ -10,11 +11,6 @@ public class SipConfig { private String ip; - /** - * 默认使用 0.0.0.0 - */ - private String monitorIp = "0.0.0.0"; - private Integer port; private String domain; @@ -25,20 +21,14 @@ public class SipConfig { Integer ptzSpeed = 50; - Integer keepaliveTimeOut = 255; - Integer registerTimeInterval = 120; - private boolean alarm = false; + private boolean alarm; public void setIp(String ip) { this.ip = ip; } - public void setMonitorIp(String monitorIp) { - this.monitorIp = monitorIp; - } - public void setPort(Integer port) { this.port = port; } @@ -59,18 +49,11 @@ public class SipConfig { this.ptzSpeed = ptzSpeed; } - public void setKeepaliveTimeOut(Integer keepaliveTimeOut) { - this.keepaliveTimeOut = keepaliveTimeOut; - } public void setRegisterTimeInterval(Integer registerTimeInterval) { this.registerTimeInterval = registerTimeInterval; } - public String getMonitorIp() { - return monitorIp; - } - public String getIp() { return ip; } @@ -99,10 +82,6 @@ public class SipConfig { return ptzSpeed; } - public Integer getKeepaliveTimeOut() { - return keepaliveTimeOut; - } - public Integer getRegisterTimeInterval() { return registerTimeInterval; } @@ -114,4 +93,10 @@ public class SipConfig { public void setAlarm(boolean alarm) { this.alarm = alarm; } + + public void getLocalIp(String deviceLocalIp) { + if (ObjectUtils.isEmpty(deviceLocalIp)) { + + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java b/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java index 41b68cc0..15e38aea 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java @@ -2,10 +2,10 @@ package com.genersoft.iot.vmp.conf; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.service.IPlatformService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; @@ -15,50 +15,42 @@ import java.util.List; /** * 系统启动时控制上级平台重新注册 + * @author lin */ @Component @Order(value=3) public class SipPlatformRunner implements CommandLineRunner { @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private IRedisCatchStorage redisCatchStorage; @Autowired - private EventPublisher publisher; + private IPlatformService platformService; @Autowired - private ZLMRTPServerFactory zlmrtpServerFactory; + private ISIPCommanderForPlatform sipCommanderForPlatform; @Override public void run(String... args) throws Exception { - // 设置所有平台离线 - storager.outlineForAllParentPlatform(); - - // 清理所有平台注册缓存 - redisCatchStorage.cleanPlatformRegisterInfos(); - - // 停止所有推流 -// zlmrtpServerFactory.closeAllSendRtpStream(); - + // 获取所有启用的平台 List parentPlatforms = storager.queryEnableParentPlatformList(true); for (ParentPlatform parentPlatform : parentPlatforms) { - redisCatchStorage.updatePlatformRegister(parentPlatform); - - redisCatchStorage.updatePlatformKeepalive(parentPlatform); - + // 更新缓存 ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); - parentPlatformCatch.setParentPlatform(parentPlatform); parentPlatformCatch.setId(parentPlatform.getServerGBId()); redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - - // 发送平台未注册消息 - publisher.platformNotRegisterEventPublish(parentPlatform.getServerGBId()); + // 设置所有平台离线 + platformService.offline(parentPlatform, true); + // 取消订阅 + sipCommanderForPlatform.unregister(parentPlatform, null, (eventResult)->{ + platformService.login(parentPlatform); + }); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java new file mode 100644 index 00000000..2cc74959 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java @@ -0,0 +1,89 @@ +package com.genersoft.iot.vmp.conf; + +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.parameters.HeaderParameter; +import org.springdoc.core.GroupedOpenApi; +import org.springdoc.core.SpringDocConfigProperties; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author lin + */ +@Configuration +public class SpringDocConfig { + + @Value("${doc.enabled: true}") + private boolean enable; + + @Bean + public OpenAPI springShopOpenApi() { + Contact contact = new Contact(); + contact.setName("pan"); + contact.setEmail("648540858@qq.com"); + return new OpenAPI() + .info(new Info().title("WVP-PRO 接口文档") + .contact(contact) + .description("开箱即用的28181协议视频平台") + .version("v2.0") + .license(new License().name("Apache 2.0").url("http://springdoc.org"))); + } + + /** + * 添加分组 + * @return + */ + @Bean + public GroupedOpenApi publicApi() { + return GroupedOpenApi.builder() + .group("1. 全部") + .packagesToScan("com.genersoft.iot.vmp.vmanager") + .build(); + } + + @Bean + public GroupedOpenApi publicApi2() { + return GroupedOpenApi.builder() + .group("2. 国标28181") + .packagesToScan("com.genersoft.iot.vmp.vmanager.gb28181") + .build(); + } + + @Bean + public GroupedOpenApi publicApi3() { + return GroupedOpenApi.builder() + .group("3. 拉流转发") + .packagesToScan("com.genersoft.iot.vmp.vmanager.streamProxy") + .build(); + } + + @Bean + public GroupedOpenApi publicApi4() { + return GroupedOpenApi.builder() + .group("4. 推流管理") + .packagesToScan("com.genersoft.iot.vmp.vmanager.streamPush") + .build(); + } + + @Bean + public GroupedOpenApi publicApi5() { + return GroupedOpenApi.builder() + .group("4. 服务管理") + .packagesToScan("com.genersoft.iot.vmp.vmanager.server") + .build(); + } + + @Bean + public GroupedOpenApi publicApi6() { + return GroupedOpenApi.builder() + .group("5. 用户管理") + .packagesToScan("com.genersoft.iot.vmp.vmanager.user") + .build(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/Swagger3Config.java b/src/main/java/com/genersoft/iot/vmp/conf/Swagger3Config.java deleted file mode 100644 index 2f0faba7..00000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/Swagger3Config.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.genersoft.iot.vmp.conf; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.service.Contact; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; - -@Configuration -public class Swagger3Config { - - @Value("${swagger-ui.enabled}") - private boolean enable; - - @Bean - public Docket createRestApi() { - return new Docket(DocumentationType.OAS_30) - .apiInfo(apiInfo()) - .groupName("1. 全部") - .select() - .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager")) - .paths(PathSelectors.any()) - .build() - .pathMapping("/") - .enable(enable); - } - @Bean - public Docket createRestGBApi() { - return new Docket(DocumentationType.OAS_30) - .apiInfo(apiInfo()) - .groupName("2. 国标28181") - .select() - .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.gb28181")) - .paths(PathSelectors.any()) - .build() - .pathMapping("/") - .enable(enable); - } - - @Bean - public Docket createRestONVIFApi() { - return new Docket(DocumentationType.OAS_30) - .apiInfo(apiInfo()) - .groupName("3. ONVIF") - .select() - .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.onvif")) - .paths(PathSelectors.any()) - .build() - .pathMapping("/") - .enable(enable); - } - - @Bean - public Docket createRestStreamProxyApi() { - return new Docket(DocumentationType.OAS_30) - .apiInfo(apiInfo()) - .groupName("4. 拉流转发") - .select() - .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.streamProxy")) - .paths(PathSelectors.any()) - .build() - .pathMapping("/") - .enable(enable); - } - @Bean - public Docket createRestStreamPushApi() { - return new Docket(DocumentationType.OAS_30) - .apiInfo(apiInfo()) - .groupName("5. 推流管理") - .select() - .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.streamPush")) - .paths(PathSelectors.any()) - .build() - .pathMapping("/") - .enable(enable); - } - - - @Bean - public Docket createServerApi() { - return new Docket(DocumentationType.OAS_30) - .apiInfo(apiInfo()) - .groupName("6. 服务管理") - .select() - .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.server")) - .paths(PathSelectors.any()) - .build() - .pathMapping("/") - .enable(enable); - } - @Bean - public Docket createUserApi() { - return new Docket(DocumentationType.OAS_30) - .apiInfo(apiInfo()) - .groupName("7. 用户管理") - .select() - .apis(RequestHandlerSelectors.basePackage("com.genersoft.iot.vmp.vmanager.user")) - .paths(PathSelectors.any()) - .build() - .pathMapping("/") - .enable(enable); - } - - private ApiInfo apiInfo() { - return new ApiInfoBuilder() - .title("WVP-PRO 接口文档") - .description("更多请咨询服务开发者(https://github.com/648540858/wvp-GB28181-pro)。") - .contact(new Contact("648540858", "648540858", "648540858@qq.com")) - .version("2.0") - .build(); - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java b/src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java new file mode 100644 index 00000000..9bc86261 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java @@ -0,0 +1,42 @@ +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * 获取系统信息写入redis + */ +@Component +public class SystemInfoTimerTask { + + private Logger logger = LoggerFactory.getLogger(SystemInfoTimerTask.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Scheduled(fixedRate = 2000) //每1秒执行一次 + public void execute(){ + try { + double cpuInfo = SystemInfoUtils.getCpuInfo(); + redisCatchStorage.addCpuInfo(cpuInfo); + double memInfo = SystemInfoUtils.getMemInfo(); + redisCatchStorage.addMemInfo(memInfo); + Map networkInterfaces = SystemInfoUtils.getNetworkInterfaces(); + redisCatchStorage.addNetInfo(networkInterfaces); + List> diskInfo =SystemInfoUtils.getDiskInfo(); + redisCatchStorage.addDiskInfo(diskInfo); + } catch (InterruptedException e) { + logger.error("[获取系统信息失败] {}", e.getMessage()); + } + + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java index 1bc85218..7377702f 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/ThreadPoolTaskConfig.java @@ -7,6 +7,10 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.ThreadPoolExecutor; +/** + * ThreadPoolTask 配置类 + * @author lin + */ @Configuration @EnableAsync(proxyTargetClass = true) public class ThreadPoolTaskConfig { @@ -31,15 +35,20 @@ public class ThreadPoolTaskConfig { * 允许线程空闲时间(单位:默认为秒) */ private static final int keepAliveTime = 30; + /** * 缓冲队列大小 */ - private static final int queueCapacity = 500; + private static final int queueCapacity = 10000; /** * 线程池名前缀 */ private static final String threadNamePrefix = "wvp-"; + /** + * + * @return + */ @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名 public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java new file mode 100644 index 00000000..a0956cc8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java @@ -0,0 +1,219 @@ +package com.genersoft.iot.vmp.conf; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 配置文件 user-settings 映射的配置信息 + */ +@Component +@ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true) +public class UserSetting { + + private Boolean savePositionHistory = Boolean.FALSE; + + private Boolean autoApplyPlay = Boolean.FALSE; + + private Boolean seniorSdp = Boolean.FALSE; + + private Integer playTimeout = 18000; + + private int platformPlayTimeout = 60000; + + private Boolean interfaceAuthentication = Boolean.TRUE; + + private Boolean recordPushLive = Boolean.TRUE; + + private Boolean recordSip = Boolean.TRUE; + + private Boolean logInDatebase = Boolean.TRUE; + + private Boolean usePushingAsStatus = Boolean.TRUE; + + private Boolean useSourceIpAsStreamIp = Boolean.FALSE; + + private Boolean sipUseSourceIpAsRemoteAddress = Boolean.FALSE; + + private Boolean streamOnDemand = Boolean.TRUE; + + private Boolean pushAuthority = Boolean.TRUE; + + private Boolean gbSendStreamStrict = Boolean.FALSE; + + private Boolean syncChannelOnDeviceOnline = Boolean.FALSE; + + private Boolean sipLog = Boolean.FALSE; + + private String serverId = "000000"; + + private String thirdPartyGBIdReg = "[\\s\\S]*"; + + private List interfaceAuthenticationExcludes = new ArrayList<>(); + + public Boolean getSavePositionHistory() { + return savePositionHistory; + } + + public Boolean isSavePositionHistory() { + return savePositionHistory; + } + + public Boolean isAutoApplyPlay() { + return autoApplyPlay; + } + + public Boolean isSeniorSdp() { + return seniorSdp; + } + + public Integer getPlayTimeout() { + return playTimeout; + } + + public Boolean isInterfaceAuthentication() { + return interfaceAuthentication; + } + + public Boolean isRecordPushLive() { + return recordPushLive; + } + + public List getInterfaceAuthenticationExcludes() { + return interfaceAuthenticationExcludes; + } + + public void setSavePositionHistory(Boolean savePositionHistory) { + this.savePositionHistory = savePositionHistory; + } + + public void setAutoApplyPlay(Boolean autoApplyPlay) { + this.autoApplyPlay = autoApplyPlay; + } + + public void setSeniorSdp(Boolean seniorSdp) { + this.seniorSdp = seniorSdp; + } + + public void setPlayTimeout(Integer playTimeout) { + this.playTimeout = playTimeout; + } + + public void setInterfaceAuthentication(boolean interfaceAuthentication) { + this.interfaceAuthentication = interfaceAuthentication; + } + + public void setRecordPushLive(Boolean recordPushLive) { + this.recordPushLive = recordPushLive; + } + + public void setInterfaceAuthenticationExcludes(List interfaceAuthenticationExcludes) { + this.interfaceAuthenticationExcludes = interfaceAuthenticationExcludes; + } + + public Boolean getLogInDatebase() { + return logInDatebase; + } + + public void setLogInDatebase(Boolean logInDatebase) { + this.logInDatebase = logInDatebase; + } + + public String getServerId() { + return serverId; + } + + public void setServerId(String serverId) { + this.serverId = serverId; + } + + public String getThirdPartyGBIdReg() { + return thirdPartyGBIdReg; + } + + public void setThirdPartyGBIdReg(String thirdPartyGBIdReg) { + this.thirdPartyGBIdReg = thirdPartyGBIdReg; + } + + public Boolean getRecordSip() { + return recordSip; + } + + public void setRecordSip(Boolean recordSip) { + this.recordSip = recordSip; + } + + public int getPlatformPlayTimeout() { + return platformPlayTimeout; + } + + public void setPlatformPlayTimeout(int platformPlayTimeout) { + this.platformPlayTimeout = platformPlayTimeout; + } + + public Boolean isUsePushingAsStatus() { + return usePushingAsStatus; + } + + public void setUsePushingAsStatus(Boolean usePushingAsStatus) { + this.usePushingAsStatus = usePushingAsStatus; + } + + public Boolean getStreamOnDemand() { + return streamOnDemand; + } + + public void setStreamOnDemand(Boolean streamOnDemand) { + this.streamOnDemand = streamOnDemand; + } + + public Boolean getUseSourceIpAsStreamIp() { + return useSourceIpAsStreamIp; + } + + public void setUseSourceIpAsStreamIp(Boolean useSourceIpAsStreamIp) { + this.useSourceIpAsStreamIp = useSourceIpAsStreamIp; + } + + public Boolean getPushAuthority() { + return pushAuthority; + } + + public void setPushAuthority(Boolean pushAuthority) { + this.pushAuthority = pushAuthority; + } + + public Boolean getGbSendStreamStrict() { + return gbSendStreamStrict; + } + + public void setGbSendStreamStrict(Boolean gbSendStreamStrict) { + this.gbSendStreamStrict = gbSendStreamStrict; + } + + public Boolean getSyncChannelOnDeviceOnline() { + return syncChannelOnDeviceOnline; + } + + public void setSyncChannelOnDeviceOnline(Boolean syncChannelOnDeviceOnline) { + this.syncChannelOnDeviceOnline = syncChannelOnDeviceOnline; + } + + public Boolean getSipUseSourceIpAsRemoteAddress() { + return sipUseSourceIpAsRemoteAddress; + } + + public void setSipUseSourceIpAsRemoteAddress(Boolean sipUseSourceIpAsRemoteAddress) { + this.sipUseSourceIpAsRemoteAddress = sipUseSourceIpAsRemoteAddress; + } + + public Boolean getSipLog() { + return sipLog; + } + + public void setSipLog(Boolean sipLog) { + this.sipLog = sipLog; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java deleted file mode 100644 index 140295e8..00000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetup.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.genersoft.iot.vmp.conf; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.List; - - -@Component -@ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true) -public class UserSetup { - - private Boolean savePositionHistory = Boolean.FALSE; - - private Boolean autoApplyPlay = Boolean.FALSE; - - private Boolean seniorSdp = Boolean.FALSE; - - private Long playTimeout = 18000L; - - private Boolean waitTrack = Boolean.FALSE; - - private Boolean interfaceAuthentication = Boolean.TRUE; - - private Boolean recordPushLive = Boolean.FALSE; - - private Boolean logInDatebase = Boolean.TRUE; - - private String serverId = "000000"; - - private String thirdPartyGBIdReg = "[\\s\\S]*"; - - private List interfaceAuthenticationExcludes = new ArrayList<>(); - - public Boolean getSavePositionHistory() { - return savePositionHistory; - } - - public Boolean isSavePositionHistory() { - return savePositionHistory; - } - - public Boolean isAutoApplyPlay() { - return autoApplyPlay; - } - - public Boolean isSeniorSdp() { - return seniorSdp; - } - - public Long getPlayTimeout() { - return playTimeout; - } - - public Boolean isWaitTrack() { - return waitTrack; - } - - public Boolean isInterfaceAuthentication() { - return interfaceAuthentication; - } - - public Boolean isRecordPushLive() { - return recordPushLive; - } - - public List getInterfaceAuthenticationExcludes() { - return interfaceAuthenticationExcludes; - } - - public void setSavePositionHistory(Boolean savePositionHistory) { - this.savePositionHistory = savePositionHistory; - } - - public void setAutoApplyPlay(Boolean autoApplyPlay) { - this.autoApplyPlay = autoApplyPlay; - } - - public void setSeniorSdp(Boolean seniorSdp) { - this.seniorSdp = seniorSdp; - } - - public void setPlayTimeout(Long playTimeout) { - this.playTimeout = playTimeout; - } - - public void setWaitTrack(Boolean waitTrack) { - this.waitTrack = waitTrack; - } - - public void setInterfaceAuthentication(boolean interfaceAuthentication) { - this.interfaceAuthentication = interfaceAuthentication; - } - - public void setRecordPushLive(Boolean recordPushLive) { - this.recordPushLive = recordPushLive; - } - - public void setInterfaceAuthenticationExcludes(List interfaceAuthenticationExcludes) { - this.interfaceAuthenticationExcludes = interfaceAuthenticationExcludes; - } - - public Boolean getLogInDatebase() { - return logInDatebase; - } - - public void setLogInDatebase(Boolean logInDatebase) { - this.logInDatebase = logInDatebase; - } - - public String getServerId() { - return serverId; - } - - public void setServerId(String serverId) { - this.serverId = serverId; - } - - public String getThirdPartyGBIdReg() { - return thirdPartyGBIdReg; - } - - public void setThirdPartyGBIdReg(String thirdPartyGBIdReg) { - this.thirdPartyGBIdReg = thirdPartyGBIdReg; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java b/src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java index 4ff5eba6..eb408ab8 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java @@ -2,35 +2,24 @@ package com.genersoft.iot.vmp.conf; import com.genersoft.iot.vmp.common.VersionPo; import com.genersoft.iot.vmp.utils.GitUtil; -import com.genersoft.iot.vmp.utils.JarFileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.util.Map; - @Component public class VersionInfo { - @Autowired - VersionConfig config; @Autowired GitUtil gitUtil; - @Autowired - JarFileUtils jarFileUtils; public VersionPo getVersion() { VersionPo versionPo = new VersionPo(); - Map map=jarFileUtils.readJarFile(); versionPo.setGIT_Revision(gitUtil.getGitCommitId()); - versionPo.setCreate_By(map.get("Created-By")); versionPo.setGIT_BRANCH(gitUtil.getBranch()); versionPo.setGIT_URL(gitUtil.getGitUrl()); versionPo.setBUILD_DATE(gitUtil.getBuildDate()); - versionPo.setArtifactId(config.getArtifactId()); versionPo.setGIT_Revision_SHORT(gitUtil.getCommitIdShort()); - versionPo.setVersion(config.getVersion()); - versionPo.setProject(config.getDescription()); - versionPo.setBuild_Jdk(map.get("Build-Jdk")); + versionPo.setVersion(gitUtil.getBuildVersion()); + versionPo.setGIT_DATE(gitUtil.getCommitTime()); return versionPo; } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java b/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java index 93e2b879..77d83ee2 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.conf; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java b/src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java new file mode 100644 index 00000000..12f6e636 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java @@ -0,0 +1,64 @@ +package com.genersoft.iot.vmp.conf.druid; + +import com.alibaba.druid.support.http.StatViewServlet; +import com.alibaba.druid.support.http.WebStatFilter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; + +import javax.servlet.Filter; +import javax.servlet.Servlet; + +/** + * druid监控配置 + * @author + */ +public class DruidConfiguration { + + @Value("${rj-druid-manage.allow:127.0.0.1}") + private String allow; + + @Value("${rj-druid-manage.deny:}") + private String deny; + + @Value("${rj-druid-manage.loginUsername:admin}") + private String loginUsername; + + @Value("${rj-druid-manage.loginPassword:admin}") + private String loginPassword; + + @Value("${rj-druid-manage.resetEnable:false}") + private String resetEnable; + + /** + * druid监控页面开启 + */ + @Bean + public ServletRegistrationBean druidServlet() { + ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); + // IP白名单 + servletRegistrationBean.addInitParameter("allow", allow); + // IP黑名单(共同存在时,deny优先于allow) + servletRegistrationBean.addInitParameter("deny", deny); + //控制台管理用户 + servletRegistrationBean.addInitParameter("loginUsername", loginUsername); + servletRegistrationBean.addInitParameter("loginPassword", loginPassword); + //是否能够重置数据 禁用HTML页面上的“Reset All”功能 + servletRegistrationBean.addInitParameter("resetEnable", resetEnable); + return servletRegistrationBean; + } + + /** + * druid url监控配置 + */ + @Bean + public FilterRegistrationBean filterRegistrationBean() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter()); + filterRegistrationBean.addUrlPatterns("/*"); + filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); + return filterRegistrationBean; + } + + +} \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java b/src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java new file mode 100644 index 00000000..5fd710be --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java @@ -0,0 +1,24 @@ +package com.genersoft.iot.vmp.conf.druid; + +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.*; + +/** + * druid监控支持注解 + * + * @author + * {@link DruidConfiguration} druid监控页面安全配置支持 + * {@link ServletComponentScan} druid监控页面需要扫描servlet + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Import({ + DruidConfiguration.class, +}) +@ServletComponentScan +public @interface EnableDruidSupport { +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/exception/ControllerException.java b/src/main/java/com/genersoft/iot/vmp/conf/exception/ControllerException.java new file mode 100644 index 00000000..d2e12069 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/exception/ControllerException.java @@ -0,0 +1,37 @@ +package com.genersoft.iot.vmp.conf.exception; + +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; + +/** + * 自定义异常,controller出现错误时直接抛出异常由全局异常捕获并返回结果 + */ +public class ControllerException extends RuntimeException{ + + private int code; + private String msg; + + public ControllerException(int code, String msg) { + this.code = code; + this.msg = msg; + } + public ControllerException(ErrorCode errorCode) { + this.code = errorCode.getCode(); + this.msg = errorCode.getMsg(); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/exception/ServiceException.java b/src/main/java/com/genersoft/iot/vmp/conf/exception/ServiceException.java new file mode 100644 index 00000000..5566d4bb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/exception/ServiceException.java @@ -0,0 +1,27 @@ +package com.genersoft.iot.vmp.conf.exception; + +/** + * @author lin + */ +public class ServiceException extends Exception{ + private String msg; + + + + public ServiceException(String msg) { + this.msg = msg; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + @Override + public String getMessage() { + return msg; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/exception/SsrcTransactionNotFoundException.java b/src/main/java/com/genersoft/iot/vmp/conf/exception/SsrcTransactionNotFoundException.java new file mode 100644 index 00000000..e1a738ab --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/exception/SsrcTransactionNotFoundException.java @@ -0,0 +1,49 @@ +package com.genersoft.iot.vmp.conf.exception; + +/** + * @author lin + */ +public class SsrcTransactionNotFoundException extends Exception{ + private String deviceId; + private String channelId; + private String callId; + private String stream; + + + + public SsrcTransactionNotFoundException(String deviceId, String channelId, String callId, String stream) { + this.deviceId = deviceId; + this.channelId = channelId; + this.callId = callId; + this.stream = stream; + } + + public String getDeviceId() { + return deviceId; + } + + public String getChannelId() { + return channelId; + } + + public String getCallId() { + return callId; + } + + public String getStream() { + return stream; + } + + @Override + public String getMessage() { + StringBuffer msg = new StringBuffer(); + msg.append(String.format("缓存事务信息未找到,device:%s channel: %s ", deviceId, channelId)); + if (callId != null) { + msg.append(",callId: " + callId); + } + if (stream != null) { + msg.append(",stream: " + stream); + } + return msg.toString(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java new file mode 100644 index 00000000..1eca1319 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java @@ -0,0 +1,87 @@ +package com.genersoft.iot.vmp.conf.redis; + + +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.service.redisMsg.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.PatternTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import com.genersoft.iot.vmp.utils.redis.FastJsonRedisSerializer; + + +/** + * @description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置 + * @author: swwheihei + * @date: 2019年5月30日 上午10:58:25 + * + */ +@Configuration +public class RedisConfig extends CachingConfigurerSupport { + + @Autowired + private RedisGpsMsgListener redisGPSMsgListener; + + @Autowired + private RedisAlarmMsgListener redisAlarmMsgListener; + + @Autowired + private RedisStreamMsgListener redisStreamMsgListener; + + @Autowired + private RedisGbPlayMsgListener redisGbPlayMsgListener; + + @Autowired + private RedisPushStreamStatusMsgListener redisPushStreamStatusMsgListener; + + @Autowired + private RedisPushStreamStatusListMsgListener redisPushStreamListMsgListener; + + @Autowired + private RedisPushStreamResponseListener redisPushStreamResponseListener; + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + // 使用fastJson序列化 + FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); + // value值的序列化采用fastJsonRedisSerializer + redisTemplate.setValueSerializer(fastJsonRedisSerializer); + redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); + + // key的序列化采用StringRedisSerializer + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory); + return redisTemplate; + } + + + /** + * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 + * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 + * + * @param connectionFactory + * @return + */ + @Bean + RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { + + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + container.addMessageListener(redisGPSMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_GPS)); + container.addMessageListener(redisAlarmMsgListener, new PatternTopic(VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE)); + container.addMessageListener(redisStreamMsgListener, new PatternTopic(VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + "PUSH")); + container.addMessageListener(redisGbPlayMsgListener, new PatternTopic(RedisGbPlayMsgListener.WVP_PUSH_STREAM_KEY)); + 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(redisPushStreamResponseListener, new PatternTopic(VideoManagerConstants.VM_MSG_STREAM_PUSH_RESPONSE)); + return container; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/runner/SipDeviceRunner.java b/src/main/java/com/genersoft/iot/vmp/conf/runner/SipDeviceRunner.java deleted file mode 100644 index 26b0a5cd..00000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/runner/SipDeviceRunner.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.genersoft.iot.vmp.conf.runner; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.CommandLineRunner; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; - -import java.util.List; - - -/** - * 系统启动时控制设备离线 - */ -@Component -@Order(value=4) -public class SipDeviceRunner implements CommandLineRunner { - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private UserSetup userSetup; - - @Override - public void run(String... args) throws Exception { - // 读取redis没有心跳信息的则设置为离线,等收到下次心跳设置为在线 - // 设置所有设备离线 - storager.outlineForAll(); - List onlineForAll = redisCatchStorage.getOnlineForAll(); - for (String deviceId : onlineForAll) { - storager.online(deviceId); - } - // 重置cseq计数 - redisCatchStorage.resetAllCSEQ(); - // TODO 查询在线设备那些开启了订阅,为设备开启定时的目录订阅 - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java b/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java index ebfac955..cd504208 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java @@ -1,6 +1,8 @@ package com.genersoft.iot.vmp.conf.security; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import org.apache.poi.hssf.eventmodel.ERFListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.AuthenticationException; @@ -13,6 +15,7 @@ import java.io.IOException; /** * 处理匿名用户访问逻辑 + * @author lin */ @Component public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint { @@ -21,16 +24,16 @@ public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoi @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { - logger.debug("用户需要登录,访问[{}]失败,AuthenticationException=[{}]", request.getRequestURI(), e.getMessage()); // 允许跨域 response.setHeader("Access-Control-Allow-Origin", "*"); // 允许自定义请求头token(允许head跨域) response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified"); response.setHeader("Content-type", "application/json;charset=UTF-8"); JSONObject jsonObject = new JSONObject(); - jsonObject.put("code", "-1"); - jsonObject.put("msg", "请登录后重新请求"); - if (request.getRequestURI().contains("api/user/login")){ + jsonObject.put("code", ErrorCode.ERROR401.getCode()); + jsonObject.put("msg", ErrorCode.ERROR401.getMsg()); + String logUri = "api/user/login"; + if (request.getRequestURI().contains(logUri)){ jsonObject.put("msg", e.getMessage()); } response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 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 63569ef1..509a1e03 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 @@ -1,9 +1,7 @@ package com.genersoft.iot.vmp.conf.security; -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 com.github.xiaoymin.knife4j.core.util.StrUtil; +import java.time.LocalDateTime; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -12,7 +10,10 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; -import java.time.LocalDateTime; +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; /** * 用户登录认证逻辑 @@ -27,7 +28,7 @@ public class DefaultUserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - if (StrUtil.isBlank(username)) { + if (StringUtils.isBlank(username)) { logger.info("登录用户:{} 不存在", username); throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java index 9690c6d1..2d7e8a1b 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java @@ -11,6 +11,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +/** + * @author lin + */ @Component public class LoginSuccessHandler implements AuthenticationSuccessHandler { diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/UrlTokenHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/UrlTokenHandler.java new file mode 100644 index 00000000..e63aca4a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/UrlTokenHandler.java @@ -0,0 +1,24 @@ +package com.genersoft.iot.vmp.conf.security; + +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import java.util.Collections; + +public class UrlTokenHandler extends SpringBootServletInitializer { + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + super.onStartup(servletContext); + + servletContext.setSessionTrackingModes( + Collections.singleton(SessionTrackingMode.COOKIE) + ); + SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig(); + sessionCookieConfig.setHttpOnly(true); + + } +} 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 f0eca856..0d3a7d6e 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,6 +1,6 @@ package com.genersoft.iot.vmp.conf.security; -import com.genersoft.iot.vmp.conf.UserSetup; +import com.genersoft.iot.vmp.conf.UserSetting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +20,7 @@ import java.util.List; /** * 配置Spring Security + * @author lin */ @Configuration @EnableWebSecurity @@ -29,7 +30,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final static Logger logger = LoggerFactory.getLogger(WebSecurityConfig.class); @Autowired - private UserSetup userSetup; + private UserSetting userSetting; @Autowired private DefaultUserDetailsServiceImpl userDetailsService; @@ -77,7 +78,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) { - if (!userSetup.isInterfaceAuthentication()) { + if (!userSetting.isInterfaceAuthentication()) { web.ignoring().antMatchers("**"); }else { // 可以直接访问的静态数据 @@ -90,8 +91,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .antMatchers("/webjars/**") .antMatchers("/swagger-resources/**") .antMatchers("/v3/api-docs/**") + .antMatchers("/favicon.ico") .antMatchers("/js/**"); - List interfaceAuthenticationExcludes = userSetup.getInterfaceAuthenticationExcludes(); + List interfaceAuthenticationExcludes = userSetting.getInterfaceAuthenticationExcludes(); for (String interfaceAuthenticationExclude : interfaceAuthenticationExcludes) { if (interfaceAuthenticationExclude.split("/").length < 4 ) { logger.warn("{}不满足两级目录,已忽略", interfaceAuthenticationExclude); @@ -132,15 +134,19 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .anyRequest().authenticated() // 异常处理(权限拒绝、登录失效等) .and().exceptionHandling() - .authenticationEntryPoint(anonymousAuthenticationEntryPoint)//匿名用户访问无权限资源时的异常处理 + //匿名用户访问无权限资源时的异常处理 + .authenticationEntryPoint(anonymousAuthenticationEntryPoint) // .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源 - // 登入 - .and().formLogin().permitAll()//允许所有用户 - .successHandler(loginSuccessHandler)//登录成功处理逻辑 - .failureHandler(loginFailureHandler)//登录失败处理逻辑 + // 登入 允许所有用户 + .and().formLogin().permitAll() + //登录成功处理逻辑 + .successHandler(loginSuccessHandler) + //登录失败处理逻辑 + .failureHandler(loginFailureHandler) // 登出 - .and().logout().logoutUrl("/api/user/logout").permitAll()//允许所有用户 - .logoutSuccessHandler(logoutHandler)//登出成功处理逻辑 + .and().logout().logoutUrl("/api/user/logout").permitAll() + //登出成功处理逻辑 + .logoutSuccessHandler(logoutHandler) .deleteCookies("JSESSIONID") // 会话管理 // .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理 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 2f62287e..605e7cf5 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java @@ -1,25 +1,26 @@ package com.genersoft.iot.vmp.gb28181; import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.conf.UserSetting; +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 org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.DependsOn; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; import javax.sip.*; -import java.util.Properties; -import java.util.TooManyListenersException; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; @Component -public class SipLayer{ +@Order(value=1) +public class SipLayer implements CommandLineRunner { private final static Logger logger = LoggerFactory.getLogger(SipLayer.class); @@ -29,82 +30,118 @@ public class SipLayer{ @Autowired private ISIPProcessorObserver sipProcessorObserver; - private SipStackImpl sipStack; + @Autowired + private UserSetting userSetting; + + private final Map tcpSipProviderMap = new ConcurrentHashMap<>(); + private final Map udpSipProviderMap = new ConcurrentHashMap<>(); private SipFactory sipFactory; + @Override + public void run(String... args) { + List monitorIps = new ArrayList<>(); + // 使用逗号分割多个ip + String separator = ","; + if (sipConfig.getIp().indexOf(separator) > 0) { + String[] split = sipConfig.getIp().split(separator); + monitorIps.addAll(Arrays.asList(split)); + }else { + monitorIps.add(sipConfig.getIp()); + } - @Bean("sipFactory") - private SipFactory createSipFactory() { sipFactory = SipFactory.getInstance(); sipFactory.setPathName("gov.nist"); - return sipFactory; - } - - @Bean("sipStack") - @DependsOn({"sipFactory"}) - private SipStack createSipStack() throws PeerUnavailableException { - Properties properties = new Properties(); - properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); - properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getMonitorIp()); - properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false"); - /** - * sip_server_log.log 和 sip_debug_log.log public static final int TRACE_NONE = - * 0; public static final int TRACE_MESSAGES = 16; public static final int - * TRACE_EXCEPTION = 17; public static final int TRACE_DEBUG = 32; - */ - properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0"); - properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log"); - properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log"); - sipStack = (SipStackImpl) sipFactory.createSipStack(properties); - return sipStack; + if (monitorIps.size() > 0) { + for (String monitorIp : monitorIps) { + addListeningPoint(monitorIp, sipConfig.getPort()); + } + if (udpSipProviderMap.size() + tcpSipProviderMap.size() == 0) { + System.exit(1); + } + } } - @Bean(name = "tcpSipProvider") - @DependsOn("sipStack") - private SipProviderImpl startTcpListener() { - ListeningPoint tcpListeningPoint = null; - SipProviderImpl tcpSipProvider = null; + private void addListeningPoint(String monitorIp, int port){ + SipStackImpl sipStack; try { - tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "TCP"); - tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint); + sipStack = (SipStackImpl)sipFactory.createSipStack(DefaultProperties.getProperties(monitorIp, false, userSetting.getSipLog())); + } catch (PeerUnavailableException e) { + logger.error("[Sip Server] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp); + return; + } + + try { + ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "TCP"); + SipProviderImpl tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint); + tcpSipProvider.setDialogErrorsAutomaticallyHandled(); tcpSipProvider.addSipListener(sipProcessorObserver); - logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "}"); - } catch (TransportNotSupportedException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - logger.error("无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用" - , sipConfig.getMonitorIp(), sipConfig.getPort()); - } catch (TooManyListenersException e) { - e.printStackTrace(); - } catch (ObjectInUseException e) { - e.printStackTrace(); + tcpSipProviderMap.put(monitorIp, tcpSipProvider); + + logger.info("[Sip Server] tcp://{}:{} 启动成功", monitorIp, port); + } catch (TransportNotSupportedException + | TooManyListenersException + | ObjectInUseException + | InvalidArgumentException e) { + logger.error("[Sip Server] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" + , monitorIp, port); } - return tcpSipProvider; - } - - @Bean(name = "udpSipProvider") - @DependsOn("sipStack") - private SipProviderImpl startUdpListener() { - ListeningPoint udpListeningPoint = null; - SipProviderImpl udpSipProvider = null; + try { - udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP"); - udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint); + ListeningPoint udpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "UDP"); + + SipProviderImpl udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint); udpSipProvider.addSipListener(sipProcessorObserver); - } catch (TransportNotSupportedException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - logger.error("无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用" - , sipConfig.getMonitorIp(), sipConfig.getPort()); - } catch (TooManyListenersException e) { - e.printStackTrace(); - } catch (ObjectInUseException e) { - e.printStackTrace(); + + udpSipProviderMap.put(monitorIp, udpSipProvider); + + logger.info("[Sip Server] udp://{}:{} 启动成功", monitorIp, port); + } catch (TransportNotSupportedException + | TooManyListenersException + | ObjectInUseException + | InvalidArgumentException e) { + logger.error("[Sip Server] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确" + , monitorIp, port); } - logger.info("Sip Server UDP 启动成功 port [" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "]"); - return udpSipProvider; } + public SipFactory getSipFactory() { + return sipFactory; + } + + public SipProviderImpl getUdpSipProvider(String ip) { + if (ObjectUtils.isEmpty(ip)) { + return null; + } + return udpSipProviderMap.get(ip); + } + + public SipProviderImpl getUdpSipProvider() { + if (udpSipProviderMap.size() != 1) { + return null; + } + return udpSipProviderMap.values().stream().findFirst().get(); + } + + public SipProviderImpl getTcpSipProvider() { + if (tcpSipProviderMap.size() != 1) { + return null; + } + return tcpSipProviderMap.values().stream().findFirst().get(); + } + + public SipProviderImpl getTcpSipProvider(String ip) { + if (ObjectUtils.isEmpty(ip)) { + return null; + } + return tcpSipProviderMap.get(ip); + } + + public String getLocalIp(String deviceLocalIp) { + if (!ObjectUtils.isEmpty(deviceLocalIp)) { + return deviceLocalIp; + } + return getUdpSipProvider().getListeningPoint().getIPAddress(); + } } 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 5ee3de69..fdb05e56 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 @@ -25,11 +25,9 @@ */ package com.genersoft.iot.vmp.gb28181.auth; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.DecimalFormat; -import java.util.Date; -import java.util.Random; +import gov.nist.core.InternalErrorHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.sip.address.URI; import javax.sip.header.AuthorizationHeader; @@ -37,10 +35,10 @@ import javax.sip.header.HeaderFactory; import javax.sip.header.WWWAuthenticateHeader; import javax.sip.message.Request; import javax.sip.message.Response; - -import gov.nist.core.InternalErrorHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.Random; /** * Implements the HTTP digest authentication method server side functionality. @@ -90,17 +88,12 @@ public class DigestServerAuthenticationHelper { * @return a generated nonce. */ private String generateNonce() { - // Get the time of day and run MD5 over it. - Date date = new Date(); - long time = date.getTime(); + long time = Instant.now().toEpochMilli(); Random rand = new Random(); long pad = rand.nextLong(); - // String nonceString = (new Long(time)).toString() - // + (new Long(pad)).toString(); String nonceString = Long.valueOf(time).toString() + Long.valueOf(pad).toString(); byte mdbytes[] = messageDigest.digest(nonceString.getBytes()); - // Convert the mdbytes array into a hex string. return toHexString(mdbytes); } @@ -129,7 +122,9 @@ public class DigestServerAuthenticationHelper { */ public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) { AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); - if ( authHeader == null ) return false; + if ( authHeader == null ) { + return false; + } String realm = authHeader.getRealm(); String username = authHeader.getUsername(); @@ -176,7 +171,9 @@ public class DigestServerAuthenticationHelper { */ public boolean doAuthenticatePlainTextPassword(Request request, String pass) { AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); - if ( authHeader == null ) return false; + if ( authHeader == null ) { + return false; + } String realm = authHeader.getRealm().trim(); String username = authHeader.getUsername().trim(); @@ -203,12 +200,13 @@ public class DigestServerAuthenticationHelper { // String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16)); String A1 = username + ":" + realm + ":" + pass; + String A2 = request.getMethod().toUpperCase() + ":" + uri.toString(); + byte mdbytes[] = messageDigest.digest(A1.getBytes()); String HA1 = toHexString(mdbytes); logger.debug("A1: " + A1); logger.debug("A2: " + A2); - mdbytes = messageDigest.digest(A2.getBytes()); String HA2 = toHexString(mdbytes); logger.debug("HA1: " + HA1); @@ -220,7 +218,7 @@ public class DigestServerAuthenticationHelper { logger.debug("qop: " + qop); String KD = HA1 + ":" + nonce; - if (qop != null && qop.equals("auth") ) { + if (qop != null && qop.equalsIgnoreCase("auth") ) { if (nc != -1) { KD += ":" + ncStr; } @@ -240,58 +238,4 @@ public class DigestServerAuthenticationHelper { } -// public static void main(String[] args) throws NoSuchAlgorithmException { -// String realm = "3402000000"; -// String username = "44010000001180008012"; - - -// String nonce = "07cab60999fbf643264ace27d3b7de8b"; -// String uri = "sip:34020000002000000001@3402000000"; -// // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略 -// String qop = "auth"; - -// // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。 -// // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护 -// //String cNonce = authHeader.getCNonce(); - -// // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量 -// int nc = 1; -// String ncStr = new DecimalFormat("00000000").format(nc); -// // String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16)); -// MessageDigest messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM); -// String A1 = username + ":" + realm + ":" + "12345678"; -// String A2 = "REGISTER" + ":" + uri; -// byte mdbytes[] = messageDigest.digest(A1.getBytes()); -// String HA1 = toHexString(mdbytes); -// System.out.println("A1: " + A1); -// System.out.println("A2: " + A2); - -// mdbytes = messageDigest.digest(A2.getBytes()); -// String HA2 = toHexString(mdbytes); -// System.out.println("HA1: " + HA1); -// System.out.println("HA2: " + HA2); -// String cnonce = "0a4f113b"; -// System.out.println("nonce: " + nonce); -// System.out.println("nc: " + ncStr); -// System.out.println("cnonce: " + cnonce); -// System.out.println("qop: " + qop); -// String KD = HA1 + ":" + nonce; - -// if (qop != null && qop.equals("auth") ) { -// if (nc != -1) { -// KD += ":" + ncStr; -// } -// if (cnonce != null) { -// KD += ":" + cnonce; -// } -// KD += ":" + qop; -// } -// KD += ":" + HA2; -// System.out.println("KD: " + KD); -// mdbytes = messageDigest.digest(KD.getBytes()); -// String mdString = toHexString(mdbytes); -// System.out.println("mdString: " + mdString); -// String response = "4f0507d4b87cdecff04bdaf4c96348f0"; -// System.out.println("response: " + response); -// } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java deleted file mode 100644 index 62d4bec0..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/RegisterLogicHandler.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.auth; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; - -/** - * @description:注册逻辑处理,当设备注册后触发逻辑。 - * @author: swwheihei - * @date: 2020年5月8日 下午9:41:46 - */ -@Component -public class RegisterLogicHandler { - - private Logger logger = LoggerFactory.getLogger(RegisterLogicHandler.class); - - @Autowired - private SIPCommander cmder; - - public void onRegister(Device device) { - // 只有第一次注册时调用查询设备信息,如需更新调用更新API接口 - if (device.isFirsRegister()) { - logger.info("[{}] 首次注册,查询设备信息以及通道信息", device.getDeviceId()); - cmder.deviceInfoQuery(device); - cmder.catalogQuery(device, null); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java new file mode 100644 index 00000000..96d25c4e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/AlarmChannelMessage.java @@ -0,0 +1,46 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +/** + * 通过redis分发报警消息 + */ +public class AlarmChannelMessage { + /** + * 国标编号 + */ + private String gbId; + + /** + * 报警编号 + */ + private int alarmSn; + + + /** + * 报警描述 + */ + private String alarmDescription; + + public String getGbId() { + return gbId; + } + + public void setGbId(String gbId) { + this.gbId = gbId; + } + + public int getAlarmSn() { + return alarmSn; + } + + public void setAlarmSn(int alarmSn) { + this.alarmSn = alarmSn; + } + + public String getAlarmDescription() { + return alarmDescription; + } + + public void setAlarmDescription(String alarmDescription) { + this.alarmDescription = alarmDescription; + } +} 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 new file mode 100644 index 00000000..965d7f2a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java @@ -0,0 +1,81 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import java.time.Instant; +import java.util.List; + +/** + * @author lin + */ +public class CatalogData { + /** + * 命令序列号 + */ + private int sn; + private int total; + private List channelList; + private Instant lastTime; + private Device device; + private String errorMsg; + + public enum CatalogDataStatus{ + 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/ChannelIdType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ChannelIdType.java new file mode 100644 index 00000000..320bbdd0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ChannelIdType.java @@ -0,0 +1,23 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +/** + * 国标类型编码,国标编码中11-13位为类型编码 + * 详见 附 录 D 编码规则 A + * @author lin + */ +public class ChannelIdType { + /** + * 中心信令控制服务器编码 + */ + public final static String CENTRAL_SIGNALING_CONTROL_SERVER = "200"; + + /** + * 业务分组编码 + */ + public final static String BUSINESS_GROUP = "215"; + + /** + * 虚拟组织编码 + */ + public final static String VIRTUAL_ORGANIZATION = "216"; +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdSendFailEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdSendFailEvent.java new file mode 100644 index 00000000..0cd4086e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdSendFailEvent.java @@ -0,0 +1,27 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import javax.sip.Dialog; +import java.util.EventObject; + +public class CmdSendFailEvent extends EventObject { + + private String callId; + + /** + * Constructs a prototypical Event. + * + * @param dialog + * @throws IllegalArgumentException if source is null. + */ + public CmdSendFailEvent(Dialog dialog) { + super(dialog); + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdType.java new file mode 100644 index 00000000..ed5cad6c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CmdType.java @@ -0,0 +1,8 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +public class CmdType { + + public static final String CATALOG = "Catalog"; + public static final String ALARM = "Alarm"; + public static final String MOBILE_POSITION = "MobilePosition"; +} 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 761437fc..e454896e 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 @@ -1,37 +1,50 @@ package com.genersoft.iot.vmp.gb28181.bean; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 国标设备/平台 + * @author lin + */ +@Schema(description = "国标设备/平台") public class Device { /** - * 设备Id + * 设备国标编号 */ + @Schema(description = "设备国标编号") private String deviceId; /** * 设备名 */ + @Schema(description = "名称") private String name; /** * 生产厂商 */ + @Schema(description = "生产厂商") private String manufacturer; /** * 型号 */ + @Schema(description = "型号") private String model; /** * 固件版本 */ + @Schema(description = "固件版本") private String firmware; /** * 传输协议 * UDP/TCP */ + @Schema(description = "传输协议(UDP/TCP)") private String transport; /** @@ -40,80 +53,140 @@ public class Device { * TCP-ACTIVE:tcp主动模式 * TCP-PASSIVE:tcp被动模式 */ + @Schema(description = "数据流传输模式") private String streamMode; /** * wan地址_ip */ + @Schema(description = "IP") private String ip; /** * wan地址_port */ + @Schema(description = "端口") private int port; /** * wan地址 */ + @Schema(description = "wan地址") private String hostAddress; /** * 在线 */ + @Schema(description = "是否在线,1为在线,0为离线") private int online; /** * 注册时间 */ + @Schema(description = "注册时间") private String registerTime; /** * 心跳时间 */ + @Schema(description = "心跳时间") private String keepaliveTime; + + /** + * 心跳间隔 + */ + @Schema(description = "心跳间隔") + private int keepaliveIntervalTime; + /** * 通道个数 */ + @Schema(description = "通道个数") private int channelCount; /** * 注册有效期 */ + @Schema(description = "注册有效期") private int expires; /** * 创建时间 */ + @Schema(description = "创建时间") private String createTime; /** * 更新时间 */ + @Schema(description = "更新时间") private String updateTime; /** * 设备使用的媒体id, 默认为null */ + @Schema(description = "设备使用的媒体id, 默认为null") private String mediaServerId; /** - * 首次注册 - */ - private boolean firsRegister; - - /** - * 字符集, 支持 utf-8 与 gb2312 + * 字符集, 支持 UTF-8 与 GB2312 */ + @Schema(description = "符集, 支持 UTF-8 与 GB2312") private String charset ; /** * 目录订阅周期,0为不订阅 */ - private int subscribeCycleForCatalog ; + @Schema(description = "目录订阅周期,0为不订阅") + private int subscribeCycleForCatalog; + /** + * 移动设备位置订阅周期,0为不订阅 + */ + @Schema(description = "移动设备位置订阅周期,0为不订阅") + private int subscribeCycleForMobilePosition; + + /** + * 移动设备位置信息上报时间间隔,单位:秒,默认值5 + */ + @Schema(description = "移动设备位置信息上报时间间隔,单位:秒,默认值5") + private int mobilePositionSubmissionInterval = 5; + + /** + * 报警订阅周期,0为不订阅 + */ + @Schema(description = "报警心跳时间订阅周期,0为不订阅") + private int subscribeCycleForAlarm; + + /** + * 是否开启ssrc校验,默认关闭,开启可以防止串流 + */ + @Schema(description = "是否开启ssrc校验,默认关闭,开启可以防止串流") + private boolean ssrcCheck = true; + + /** + * 地理坐标系, 目前支持 WGS84,GCJ02 + */ + @Schema(description = "地理坐标系, 目前支持 WGS84,GCJ02") + private String geoCoordSys; + + /** + * 树类型 国标规定了两种树的展现方式 行政区划:CivilCode 和业务分组:BusinessGroup + */ + @Schema(description = "树类型 国标规定了两种树的展现方式 行政区划:CivilCode 和业务分组:BusinessGroup") + private String treeType; + + @Schema(description = "密码") + private String password; + + @Schema(description = "收流IP") + private String sdpIp; + + @Schema(description = "SIP交互IP(设备访问平台的IP)") + private String localIp; public String getDeviceId() { @@ -260,14 +333,6 @@ public class Device { this.mediaServerId = mediaServerId; } - public boolean isFirsRegister() { - return firsRegister; - } - - public void setFirsRegister(boolean firsRegister) { - this.firsRegister = firsRegister; - } - public String getCharset() { return charset; } @@ -283,4 +348,84 @@ public class Device { 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 getTreeType() { + return treeType; + } + + public void setTreeType(String treeType) { + this.treeType = treeType; + } + + 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; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java index 8176c1c9..bfc97b55 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java @@ -1,59 +1,101 @@ package com.genersoft.iot.vmp.gb28181.bean; +import io.swagger.v3.oas.annotations.media.Schema; +/** + * @author lin + */ +@Schema(description = "报警信息") public class DeviceAlarm { /** * 数据库id */ + @Schema(description = "数据库id") private String id; /** * 设备Id */ + @Schema(description = "设备的国标编号") private String deviceId; /** * 通道Id */ + @Schema(description = "通道的国标编号") private String channelId; /** - * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- + * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情 */ + @Schema(description = "报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情") private String alarmPriority; /** * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, * 7其他报警;可以为直接组合如12为电话报警或 设备报警- */ + @Schema(description = "报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,\n" + + "\t * 7其他报警;可以为直接组合如12为电话报警或设备报警") private String alarmMethod; /** * 报警时间 */ + @Schema(description = "报警时间") private String alarmTime; /** * 报警内容描述 */ + @Schema(description = "报警内容描述") private String alarmDescription; /** * 经度 */ + @Schema(description = "经度") private double longitude; /** * 纬度 */ + @Schema(description = "纬度") private double latitude; /** - * 报警类型 + * 报警类型, + * 报警方式为2时,不携带 AlarmType为默认的报警设备报警, + * 携带 AlarmType取值及对应报警类型如下: + * 1-视频丢失报警; + * 2-设备防拆报警; + * 3-存储设备磁盘满报警; + * 4-设备高温报警; + * 5-设备低温报警。 + * 报警方式为5时,取值如下: + * 1-人工视频报警; + * 2-运动目标检测报警; + * 3-遗留物检测报警; + * 4-物体移除检测报警; + * 5-绊线检测报警; + * 6-入侵检测报警; + * 7-逆行检测报警; + * 8-徘徊检测报警; + * 9-流量统计报警; + * 10-密度检测报警; + * 11-视频异常检测报警; + * 12-快速移动报警。 + * 报警方式为6时,取值下: + * 1-存储设备磁盘故障报警; + * 2-存储设备风扇故障报警。 */ + @Schema(description = "报警类型") private String alarmType; + @Schema(description = "创建时间") + private String createTime; + public String getId() { return id; @@ -134,4 +176,12 @@ public class DeviceAlarm { public void setChannelId(String channelId) { this.channelId = channelId; } + + 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/DeviceAlarmMethod.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java new file mode 100644 index 00000000..ff8761ee --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarmMethod.java @@ -0,0 +1,40 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +/** + * 报警方式 + * @author lin + * 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, + * 7其他报警;可以为直接组合如12为电话报警或 设备报警- + */ +public enum DeviceAlarmMethod { + // 1为电话报警 + Telephone(1), + + // 2为设备报警 + Device(2), + + // 3为短信报警 + SMS(3), + + // 4为 GPS报警 + GPS(4), + + // 5为视频报警 + Video(5), + + // 6为设备故障报警 + DeviceFailure(6), + + // 7其他报警 + Other(7); + + private final int val; + + DeviceAlarmMethod(int val) { + this.val=val; + } + + public int getVal() { + return val; + } +} 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 6d40e360..0e1b6181 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,132 +1,165 @@ package com.genersoft.iot.vmp.gb28181.bean; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "通道信息") public class DeviceChannel { + /** + * 数据库自增ID + */ + @Schema(description = "数据库自增ID") + private int id; /** - * 通道id + * 通道国标编号 */ + @Schema(description = "通道国标编号") private String channelId; /** - * 设备id + * 设备国标编号 */ + @Schema(description = "设备国标编号") private String deviceId; /** * 通道名 */ + @Schema(description = "名称") private String name; /** * 生产厂商 */ + @Schema(description = "生产厂商") private String manufacture; /** * 型号 */ + @Schema(description = "型号") private String model; /** * 设备归属 */ + @Schema(description = "设备归属") private String owner; /** * 行政区域 */ + @Schema(description = "行政区域") private String civilCode; /** * 警区 */ + @Schema(description = "警区") private String block; /** * 安装地址 */ + @Schema(description = "安装地址") private String address; /** * 是否有子设备 1有, 0没有 */ + @Schema(description = "是否有子设备 1有, 0没有") private int parental; /** * 父级id */ + @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; /** * 证书序列号 */ + @Schema(description = "证书序列号") private String certNum; /** * 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效 */ + @Schema(description = "证书有效标识 缺省为0;证书有效标识:0:无效1: 有效") private int certifiable; /** * 证书无效原因码 */ + @Schema(description = "证书无效原因码") private int errCode; /** * 证书终止有效期 */ + @Schema(description = "证书终止有效期") private String endTime; /** * 保密属性 缺省为0; 0:不涉密, 1:涉密 */ + @Schema(description = "保密属性 缺省为0; 0:不涉密, 1:涉密") private String secrecy; /** * IP地址 */ + @Schema(description = "IP地址") private String ipAddress; /** * 端口号 */ + @Schema(description = "端口号") private int port; /** * 密码 */ + @Schema(description = "密码") private String password; /** * 云台类型 */ + @Schema(description = "云台类型") private int PTZType; /** * 云台类型描述字符串 */ + @Schema(description = "云台类型描述字符串") private String PTZTypeText; /** * 创建时间 */ + @Schema(description = "创建时间") private String createTime; /** * 更新时间 */ + @Schema(description = "更新时间") private String updateTime; /** @@ -138,33 +171,89 @@ public class DeviceChannel { * OFF * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF */ + @Schema(description = "在线/离线, 1在线,0离线") private int status; /** * 经度 */ + @Schema(description = "经度") private double longitude; /** * 纬度 */ + @Schema(description = "纬度") private double latitude; + /** + * 经度 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; + /** + * 标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划 + */ + @Schema(description = "标记通道的类型,0->国标通道 1->直播流通道 2->业务分组/虚拟组织/行政区划") + private int channelType; + + /** + * 业务分组 + */ + @Schema(description = "业务分组") + private String businessGroupId; + + /** + * GPS的更新时间 + */ + @Schema(description = "GPS的更新时间") + private String gpsTime; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + public String getDeviceId() { return deviceId; } @@ -390,6 +479,38 @@ public class DeviceChannel { 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; } @@ -429,4 +550,28 @@ public class DeviceChannel { 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; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannelInPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannelInPlatform.java new file mode 100644 index 00000000..c61bb088 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceChannelInPlatform.java @@ -0,0 +1,23 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +public class DeviceChannelInPlatform extends DeviceChannel{ + + private String platFormId; + private String catalogId; + + 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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceNotFoundEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceNotFoundEvent.java new file mode 100644 index 00000000..c782c3c1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceNotFoundEvent.java @@ -0,0 +1,27 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import javax.sip.Dialog; +import java.util.EventObject; + +public class DeviceNotFoundEvent extends EventObject { + + private String callId; + + /** + * Constructs a prototypical Event. + * + * @param dialog + * @throws IllegalArgumentException if source is null. + */ + public DeviceNotFoundEvent(Dialog dialog) { + super(dialog); + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java index a1ce8ca9..d1e4ccbd 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GbStream.java @@ -1,20 +1,48 @@ package com.genersoft.iot.vmp.gb28181.bean; +import io.swagger.v3.oas.annotations.media.Schema; + /** * 直播流关联国标上级平台 + * @author lin */ +@Schema(description = "直播流关联国标上级平台") public class GbStream extends PlatformGbStream{ + @Schema(description = "ID") + private Integer gbStreamId; + @Schema(description = "应用名") private String app; + @Schema(description = "流ID") private String stream; + @Schema(description = "国标ID") private String gbId; + @Schema(description = "名称") private String name; + @Schema(description = "流媒体ID") private String mediaServerId; + @Schema(description = "经度") private double longitude; + @Schema(description = "纬度") private double latitude; + @Schema(description = "流类型(拉流/推流)") private String streamType; + @Schema(description = "状态") private boolean status; + @Schema(description = "创建时间") + public String createTime; + + @Override + public Integer getGbStreamId() { + return gbStreamId; + } + + @Override + public void setGbStreamId(Integer gbStreamId) { + this.gbStreamId = gbStreamId; + } + public String getApp() { return app; } @@ -86,4 +114,12 @@ public class GbStream extends PlatformGbStream{ public void setMediaServerId(String mediaServerId) { this.mediaServerId = mediaServerId; } + + 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/HandlerCatchData.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HandlerCatchData.java new file mode 100644 index 00000000..97da8630 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/HandlerCatchData.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import org.dom4j.Element; + +import javax.sip.RequestEvent; + +/** + * @author lin + */ +public class HandlerCatchData { + private RequestEvent evt; + private Device device; + private Element rootElement; + + public HandlerCatchData(RequestEvent evt, Device device, Element rootElement) { + this.evt = evt; + this.device = device; + this.rootElement = rootElement; + } + + public RequestEvent getEvt() { + return evt; + } + + public void setEvt(RequestEvent evt) { + this.evt = evt; + } + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + public Element getRootElement() { + return rootElement; + } + + public void setRootElement(Element rootElement) { + this.rootElement = rootElement; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamCallback.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamCallback.java new file mode 100644 index 00000000..42a05198 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamCallback.java @@ -0,0 +1,5 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +public interface InviteStreamCallback { + void call(InviteStreamInfo inviteStreamInfo); +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamInfo.java new file mode 100644 index 00000000..134930b3 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamInfo.java @@ -0,0 +1,61 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; + +public class InviteStreamInfo { + + public InviteStreamInfo(MediaServerItem mediaServerItem, JSONObject response, String callId, String app, String stream) { + this.mediaServerItem = mediaServerItem; + this.response = response; + this.callId = callId; + this.app = app; + this.stream = stream; + } + + private MediaServerItem mediaServerItem; + private JSONObject response; + private String callId; + private String app; + private String stream; + + public MediaServerItem getMediaServerItem() { + return mediaServerItem; + } + + public void setMediaServerItem(MediaServerItem mediaServerItem) { + this.mediaServerItem = mediaServerItem; + } + + public JSONObject getResponse() { + return response; + } + + public void setResponse(JSONObject response) { + this.response = response; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + 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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java new file mode 100644 index 00000000..24d509fd --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamType.java @@ -0,0 +1,8 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +public enum InviteStreamType { + + PLAY,PLAYBACK,PUSH,PROXY,CLOUD_RECORD_PUSH,CLOUD_RECORD_PROXY + + +} 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 c6cf7825..19000930 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/MobilePosition.java @@ -57,20 +57,30 @@ public class MobilePosition { */ private String reportSource; - /** - * 国内地理坐标系(GCJ-02 / BD-09) - */ - private String GeodeticSystem; - /** * 国内坐标系:经度坐标 */ - private String cnLng; + private double longitudeGcj02; /** * 国内坐标系:纬度坐标 */ - private String cnLat; + private double latitudeGcj02; + + /** + * 国内坐标系:经度坐标 + */ + private double longitudeWgs84; + + /** + * 国内坐标系:纬度坐标 + */ + private double latitudeWgs84; + + /** + * 创建时间 + */ + private String createTime; public String getDeviceId() { @@ -145,30 +155,6 @@ public class MobilePosition { this.reportSource = reportSource; } - public String getGeodeticSystem() { - return GeodeticSystem; - } - - public void setGeodeticSystem(String geodeticSystem) { - GeodeticSystem = geodeticSystem; - } - - public String getCnLng() { - return cnLng; - } - - public void setCnLng(String cnLng) { - this.cnLng = cnLng; - } - - public String getCnLat() { - return cnLat; - } - - public void setCnLat(String cnLat) { - this.cnLat = cnLat; - } - public String getChannelId() { return channelId; } @@ -176,4 +162,44 @@ public class MobilePosition { 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/ParentPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java index fabae8a1..b056cc71 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java @@ -1,113 +1,193 @@ 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 String devicePort; /** * SIP认证用户名(默认使用设备国标编号) */ + @Schema(description = "SIP认证用户名(默认使用设备国标编号)") private String username; /** * SIP认证密码 */ + @Schema(description = "SIP认证密码") private String password; /** * 注册周期 (秒) */ - private String expires; + @Schema(description = "注册周期 (秒)") + private int expires; /** * 心跳周期(秒) */ - private String keepTimeout; + @Schema(description = "心跳周期(秒)") + private int keepTimeout; /** * 传输协议 * UDP/TCP */ + @Schema(description = "传输协议") private String transport; /** * 字符集 */ + @Schema(description = "字符集") private String characterSet; /** * 允许云台控制 */ + @Schema(description = "允许云台控制") private boolean ptz; /** * RTCP流保活 - * TODO 预留, 暂不实现 */ + @Schema(description = "RTCP流保活") private boolean rtcp; /** * 在线状态 */ + @Schema(description = "在线状态") private boolean status; /** * 在线状态 */ + @Schema(description = "在线状态") private int channelCount; /** - * 共享所有的直播流 + * 默认目录Id,自动添加的通道多放在这个目录下 */ - private boolean shareAllLiveStream; + @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; + + /** + * 树类型 国标规定了两种树的展现方式 行政区划 CivilCode 和业务分组:BusinessGroup + */ + @Schema(description = "树类型 国标规定了两种树的展现方式 行政区划 CivilCode 和业务分组:BusinessGrou") + private String treeType; public Integer getId() { return id; @@ -205,19 +285,19 @@ public class ParentPlatform { this.password = password; } - public String getExpires() { + public int getExpires() { return expires; } - public void setExpires(String expires) { + public void setExpires(int expires) { this.expires = expires; } - public String getKeepTimeout() { + public int getKeepTimeout() { return keepTimeout; } - public void setKeepTimeout(String keepTimeout) { + public void setKeepTimeout(int keepTimeout) { this.keepTimeout = keepTimeout; } @@ -269,12 +349,83 @@ public class ParentPlatform { this.channelCount = channelCount; } - - public boolean isShareAllLiveStream() { - return shareAllLiveStream; + public String getCatalogId() { + return catalogId; } - public void setShareAllLiveStream(boolean shareAllLiveStream) { - this.shareAllLiveStream = shareAllLiveStream; + 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 String getTreeType() { + return treeType; + } + + public void setTreeType(String treeType) { + this.treeType = treeType; } } 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 index 6c429f26..a53d26e4 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java @@ -4,7 +4,9 @@ public class ParentPlatformCatch { private String id; - // 心跳未回复次数 + /** + * 心跳未回复次数 + */ private int keepAliveReply; // 注册未回复次数 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java new file mode 100644 index 00000000..38ba2f03 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformCatalog.java @@ -0,0 +1,116 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 国标级联-目录 + * @author lin + */ +@Schema(description = "目录信息") +public class PlatformCatalog { + @Schema(description = "ID") + private String id; + + @Schema(description = "名称") + private String name; + + @Schema(description = "平台ID") + private String platformId; + + @Schema(description = "父级目录ID") + private String parentId; + + @Schema(description = "行政区划") + private String civilCode; + + @Schema(description = "目录分组") + private String businessGroupId; + + /** + * 子节点数 + */ + @Schema(description = "子节点数") + private int childrenCount; + + /** + * 0 目录, 1 国标通道, 2 直播流 + */ + @Schema(description = "类型:0 目录, 1 国标通道, 2 直播流") + private int type; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public int getChildrenCount() { + return childrenCount; + } + + public void setChildrenCount(int childrenCount) { + this.childrenCount = childrenCount; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public void setTypeForCatalog() { + this.type = 0; + } + + public void setTypeForGb() { + this.type = 1; + } + + public void setTypeForStream() { + this.type = 2; + } + + public String getCivilCode() { + return civilCode; + } + + public void setCivilCode(String civilCode) { + this.civilCode = civilCode; + } + + public String getBusinessGroupId() { + return businessGroupId; + } + + public void setBusinessGroupId(String businessGroupId) { + this.businessGroupId = businessGroupId; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformGbStream.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformGbStream.java index a4f7730d..d52cf7bd 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformGbStream.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PlatformGbStream.java @@ -1,24 +1,24 @@ package com.genersoft.iot.vmp.gb28181.bean; +import io.swagger.v3.oas.annotations.media.Schema; + public class PlatformGbStream { - private String app; - private String stream; + + @Schema(description = "ID") + private Integer gbStreamId; + + @Schema(description = "平台ID") private String platformId; - public String getApp() { - return app; + @Schema(description = "目录ID") + private String catalogId; + + public Integer getGbStreamId() { + return gbStreamId; } - public void setApp(String app) { - this.app = app; - } - - public String getStream() { - return stream; - } - - public void setStream(String stream) { - this.stream = stream; + public void setGbStreamId(Integer gbStreamId) { + this.gbStreamId = gbStreamId; } public String getPlatformId() { @@ -29,4 +29,11 @@ public class PlatformGbStream { this.platformId = platformId; } + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PresetQuerySipReq.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PresetQuerySipReq.java new file mode 100644 index 00000000..d1971a2e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/PresetQuerySipReq.java @@ -0,0 +1,28 @@ +package com.genersoft.iot.vmp.gb28181.bean; + + +/** + * @author chenjialing + */ +public class PresetQuerySipReq { + + private String presetId; + + private String presetName; + + public String getPresetId() { + return presetId; + } + + public void setPresetId(String presetId) { + this.presetId = presetId; + } + + public String getPresetName() { + return presetName; + } + + public void setPresetName(String presetName) { + this.presetName = presetName; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java index 24fc2212..2121db7a 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java @@ -1,8 +1,6 @@ package com.genersoft.iot.vmp.gb28181.bean; - -//import gov.nist.javax.sip.header.SIPDate; - +import java.time.Instant; import java.util.List; /** @@ -21,6 +19,8 @@ public class RecordInfo { private String name; private int sumNum; + + private Instant lastTime; private List recordList; @@ -71,4 +71,12 @@ public class RecordInfo { public void setSn(String sn) { this.sn = sn; } + + public Instant getLastTime() { + return lastTime; + } + + public void setLastTime(Instant lastTime) { + this.lastTime = lastTime; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java index 39f894ce..a47147a1 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java @@ -1,11 +1,12 @@ package com.genersoft.iot.vmp.gb28181.bean; +import com.genersoft.iot.vmp.utils.DateUtil; import org.jetbrains.annotations.NotNull; import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.time.Instant; +import java.time.temporal.TemporalAccessor; /** * @description:设备录像bean @@ -19,7 +20,9 @@ public class RecordItem implements Comparable{ private String name; private String filePath; - + + private String fileSize; + private String address; private String startTime; @@ -104,20 +107,27 @@ public class RecordItem implements Comparable{ this.recorderId = recorderId; } + public String getFileSize() { + return fileSize; + } + + public void setFileSize(String fileSize) { + this.fileSize = fileSize; + } + @Override public int compareTo(@NotNull RecordItem recordItem) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - try { - Date startTime_now = sdf.parse(startTime); - Date startTime_param = sdf.parse(recordItem.getStartTime()); - if (startTime_param.compareTo(startTime_now) > 0) { - return -1; - }else { - return 1; - } - } catch (ParseException e) { - e.printStackTrace(); + TemporalAccessor startTimeNow = DateUtil.formatter.parse(startTime); + TemporalAccessor startTimeParam = DateUtil.formatter.parse(recordItem.getStartTime()); + Instant startTimeParamInstant = Instant.from(startTimeParam); + Instant startTimeNowInstant = Instant.from(startTimeNow); + if (startTimeNowInstant.equals(startTimeParamInstant)) { + return 0; + }else if (Instant.from(startTimeParam).isAfter(Instant.from(startTimeNow)) ) { + return -1; + }else { + return 1; } - return 0; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RemoteAddressInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RemoteAddressInfo.java new file mode 100644 index 00000000..4107593f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RemoteAddressInfo.java @@ -0,0 +1,27 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +public class RemoteAddressInfo { + private String ip; + private int port; + + public RemoteAddressInfo(String ip, int port) { + this.ip = ip; + this.port = port; + } + + 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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SDPInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SDPInfo.java new file mode 100644 index 00000000..39225b57 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SDPInfo.java @@ -0,0 +1,14 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import javax.sdp.SessionDescription; + +public class SDPInfo { + private byte[] source; + private SessionDescription sdpSource; + private String sessionName; + private Long startTime; + private Long stopTime; + private String username; + private String address; + private String ssrc; +} 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 index 2c9c494c..c1fe2c1f 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SendRtpItem.java @@ -71,6 +71,52 @@ public class SendRtpItem { */ 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; + public String getIp() { return ip; } @@ -174,4 +220,76 @@ public class SendRtpItem { public void setMediaServerId(String mediaServerId) { this.mediaServerId = mediaServerId; } + + public String getCallId() { + return CallId; + } + + public void setCallId(String callId) { + 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; + } } 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 new file mode 100644 index 00000000..302539b6 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipMsgInfo.java @@ -0,0 +1,56 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import org.dom4j.Element; + +import javax.sip.RequestEvent; + +public class SipMsgInfo { + private RequestEvent evt; + private Device device; + private ParentPlatform platform; + private Element rootElement; + + public SipMsgInfo(RequestEvent evt, Device device, Element rootElement) { + this.evt = evt; + this.device = device; + this.rootElement = rootElement; + } + + public SipMsgInfo(RequestEvent evt, ParentPlatform platform, Element rootElement) { + this.evt = evt; + this.platform = platform; + this.rootElement = rootElement; + } + + public RequestEvent getEvt() { + return evt; + } + + public void setEvt(RequestEvent evt) { + this.evt = evt; + } + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + public ParentPlatform getPlatform() { + return platform; + } + + public void setPlatform(ParentPlatform platform) { + this.platform = platform; + } + + public Element getRootElement() { + return rootElement; + } + + public void setRootElement(Element rootElement) { + this.rootElement = rootElement; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipTransactionInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipTransactionInfo.java new file mode 100644 index 00000000..c04a6959 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SipTransactionInfo.java @@ -0,0 +1,54 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; + +public class SipTransactionInfo { + + private String callId; + private String fromTag; + private String toTag; + private String viaBranch; + + public SipTransactionInfo(SIPResponse response) { + this.callId = response.getCallIdHeader().getCallId(); + this.fromTag = response.getFromTag(); + this.toTag = response.getToTag(); + this.viaBranch = response.getTopmostViaHeader().getBranch(); + } + + public SipTransactionInfo() { + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + 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 String getViaBranch() { + return viaBranch; + } + + public void setViaBranch(String viaBranch) { + this.viaBranch = viaBranch; + } +} 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 9700f8a5..d27ce262 100644 --- 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,14 +1,19 @@ package com.genersoft.iot.vmp.gb28181.bean; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; + public class SsrcTransaction { private String deviceId; private String channelId; - private String ssrc; - private String streamId; - private byte[] transaction; - private byte[] dialog; + private String callId; + private String stream; private String mediaServerId; + private String ssrc; + + private SipTransactionInfo sipTransactionInfo; + + private VideoStreamSessionManager.SessionType type; public String getDeviceId() { return deviceId; @@ -26,36 +31,20 @@ public class SsrcTransaction { this.channelId = channelId; } - public String getSsrc() { - return ssrc; + public String getCallId() { + return callId; } - public void setSsrc(String ssrc) { - this.ssrc = ssrc; + public void setCallId(String callId) { + this.callId = callId; } - public String getStreamId() { - return streamId; + public String getStream() { + return stream; } - public void setStreamId(String streamId) { - this.streamId = streamId; - } - - public byte[] getTransaction() { - return transaction; - } - - public void setTransaction(byte[] transaction) { - this.transaction = transaction; - } - - public byte[] getDialog() { - return dialog; - } - - public void setDialog(byte[] dialog) { - this.dialog = dialog; + public void setStream(String stream) { + this.stream = stream; } public String getMediaServerId() { @@ -65,4 +54,28 @@ public class SsrcTransaction { public void setMediaServerId(String mediaServerId) { this.mediaServerId = mediaServerId; } + + public String getSsrc() { + return ssrc; + } + + public void setSsrc(String ssrc) { + this.ssrc = ssrc; + } + + public VideoStreamSessionManager.SessionType getType() { + return type; + } + + public void setType(VideoStreamSessionManager.SessionType type) { + this.type = type; + } + + 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/SubscribeHolder.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java new file mode 100644 index 00000000..ba905b50 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeHolder.java @@ -0,0 +1,106 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; +import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.service.IPlatformService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author lin + */ +@Component +public class SubscribeHolder { + + @Autowired + private DynamicTask dynamicTask; + + private final String taskOverduePrefix = "subscribe_overdue_"; + + private static ConcurrentHashMap catalogMap = new ConcurrentHashMap<>(); + private static ConcurrentHashMap mobilePositionMap = new ConcurrentHashMap<>(); + + + public void putCatalogSubscribe(String platformId, SubscribeInfo subscribeInfo) { + catalogMap.put(platformId, subscribeInfo); + // 添加订阅到期 + String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId; + // 添加任务处理订阅过期 + dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()), + subscribeInfo.getExpires() * 1000); + } + + public SubscribeInfo getCatalogSubscribe(String platformId) { + return catalogMap.get(platformId); + } + + public void removeCatalogSubscribe(String platformId) { + + catalogMap.remove(platformId); + String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId; + Runnable runnable = dynamicTask.get(taskOverdueKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + // 添加任务处理订阅过期 + dynamicTask.stop(taskOverdueKey); + } + + public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo) { + mobilePositionMap.put(platformId, subscribeInfo); + String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + "MobilePosition_" + platformId; + // 添加任务处理GPS定时推送 + dynamicTask.startCron(key, new MobilePositionSubscribeHandlerTask(platformId), + subscribeInfo.getGpsInterval() * 1000); + String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; + // 添加任务处理订阅过期 + dynamicTask.startDelay(taskOverdueKey, () -> { + removeMobilePositionSubscribe(subscribeInfo.getId()); + }, + subscribeInfo.getExpires() * 1000); + } + + public SubscribeInfo getMobilePositionSubscribe(String platformId) { + return mobilePositionMap.get(platformId); + } + + public void removeMobilePositionSubscribe(String platformId) { + mobilePositionMap.remove(platformId); + String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + "MobilePosition_" + platformId; + // 结束任务处理GPS定时推送 + dynamicTask.stop(key); + String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; + Runnable runnable = dynamicTask.get(taskOverdueKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + // 添加任务处理订阅过期 + dynamicTask.stop(taskOverdueKey); + } + + public List getAllCatalogSubscribePlatform() { + List platforms = new ArrayList<>(); + if(catalogMap.size() > 0) { + for (String key : catalogMap.keySet()) { + platforms.add(catalogMap.get(key).getId()); + } + } + return platforms; + } + + public void removeAllSubscribe(String platformId) { + removeMobilePositionSubscribe(platformId); + removeCatalogSubscribe(platformId); + } +} 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 new file mode 100644 index 00000000..07176f21 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java @@ -0,0 +1,99 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; + +import javax.sip.header.*; + +public class SubscribeInfo { + + + public SubscribeInfo(SIPRequest request, String id) { + this.id = id; + this.request = request; + this.expires = request.getExpires().getExpires(); + EventHeader eventHeader = (EventHeader)request.getHeader(EventHeader.NAME); + this.eventId = eventHeader.getEventId(); + this.eventType = eventHeader.getEventType(); + + } + + private String id; + + private SIPRequest request; + private int expires; + private String eventId; + private String eventType; + private SIPResponse response; + + /** + * 以下为可选字段 + * @return + */ + private String sn; + private int gpsInterval; + + public String getId() { + return id; + } + + 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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SyncStatus.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SyncStatus.java new file mode 100644 index 00000000..373b971c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/SyncStatus.java @@ -0,0 +1,51 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * 摄像机同步状态 + * @author lin + */ +@Schema(description = "摄像机同步状态") +public class SyncStatus { + @Schema(description = "总数") + private int total; + @Schema(description = "当前更新多少") + private int current; + @Schema(description = "错误描述") + private String errorMsg; + @Schema(description = "是否同步中") + private boolean syncIng; + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public int getCurrent() { + return current; + } + + public void setCurrent(int current) { + this.current = current; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public boolean isSyncIng() { + return syncIng; + } + + public void setSyncIng(boolean syncIng) { + this.syncIng = syncIng; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/TreeType.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/TreeType.java new file mode 100644 index 00000000..bb684e1a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/TreeType.java @@ -0,0 +1,10 @@ +package com.genersoft.iot.vmp.gb28181.bean; + +/** + * 目录结构类型 + * @author lin + */ +public class TreeType { + public static final String BUSINESS_GROUP = "BusinessGroup"; + public static final String CIVIL_CODE = "CivilCode"; +} 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 new file mode 100644 index 00000000..3cdf48dc --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/conf/DefaultProperties.java @@ -0,0 +1,78 @@ +package com.genersoft.iot.vmp.gb28181.conf; + +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd.AlarmNotifyMessageHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Properties; + +/** + * 获取sip默认配置 + * @author lin + */ +public class DefaultProperties { + + public static Properties getProperties(String ip, boolean isDebug, boolean sipLog) { + Properties properties = new Properties(); + properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP"); + properties.setProperty("javax.sip.IP_ADDRESS", ip); + // 关闭自动会话 + properties.setProperty("javax.sip.AUTOMATIC_DIALOG_SUPPORT", "off"); + /** + * 完整配置参考 gov.nist.javax.sip.SipStackImpl,需要下载源码 + * gov/nist/javax/sip/SipStackImpl.class + * sip消息的解析在 gov.nist.javax.sip.stack.UDPMessageChannel的processIncomingDataPacket方法 + */ + +// * gov/nist/javax/sip/SipStackImpl.class + if (isDebug) { + properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false"); + } + // 接收所有notify请求,即使没有订阅 + properties.setProperty("gov.nist.javax.sip.DELIVER_UNSOLICITED_NOTIFY", "true"); + properties.setProperty("gov.nist.javax.sip.AUTOMATIC_DIALOG_ERROR_HANDLING", "false"); + properties.setProperty("gov.nist.javax.sip.CANCEL_CLIENT_TRANSACTION_CHECKED", "true"); + // 为_NULL _对话框传递_终止的_事件 + properties.setProperty("gov.nist.javax.sip.DELIVER_TERMINATED_EVENT_FOR_NULL_DIALOG", "true"); + // 会话清理策略 + properties.setProperty("gov.nist.javax.sip.RELEASE_REFERENCES_STRATEGY", "Normal"); + // 处理由该服务器处理的基于底层TCP的保持生存超时 + properties.setProperty("gov.nist.javax.sip.RELIABLE_CONNECTION_KEEP_ALIVE_TIMEOUT", "60"); + // 获取实际内容长度,不使用header中的长度信息 + properties.setProperty("gov.nist.javax.sip.COMPUTE_CONTENT_LENGTH_FROM_MESSAGE_BODY", "true"); + // 线程可重入 + properties.setProperty("gov.nist.javax.sip.REENTRANT_LISTENER", "true"); + // 定义应用程序打算多久审计一次 SIP 堆栈,了解其内部线程的健康状况(该属性指定连续审计之间的时间(以毫秒为单位)) + properties.setProperty("gov.nist.javax.sip.THREAD_AUDIT_INTERVAL_IN_MILLISECS", "30000"); + + /** + * sip_server_log.log 和 sip_debug_log.log ERROR, INFO, WARNING, OFF, DEBUG, TRACE + */ + Logger logger = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class); + if (sipLog) { + if (logger.isDebugEnabled()) { + System.out.println("DEBUG"); + properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "DEBUG"); + }else if (logger.isInfoEnabled()) { + System.out.println("INFO1"); + properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "INFO"); + }else if (logger.isWarnEnabled()) { + System.out.println("WARNING"); + properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "WARNING"); + }else if (logger.isErrorEnabled()) { + System.out.println("ERROR"); + properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "ERROR"); + }else { + System.out.println("INFO2"); + properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "INFO"); + } + logger.info("[SIP日志]级别为: {}", properties.getProperty("gov.nist.javax.sip.TRACE_LEVEL")); + }else { + logger.info("[SIP日志]已关闭"); + } + + + + return properties; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java deleted file mode 100644 index ea2fa6c2..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/DeviceOffLineDetector.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event; - -import com.genersoft.iot.vmp.conf.UserSetup; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -/** - * @description:设备离在线状态检测器,用于检测设备状态 - * @author: swwheihei - * @date: 2020年5月13日 下午2:40:29 - */ -@Component -public class DeviceOffLineDetector { - - @Autowired - private RedisUtil redis; - - @Autowired - private UserSetup userSetup; - - public boolean isOnline(String deviceId) { - String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + deviceId; - return redis.hasKey(key); - } -} 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 9495e9de..26ababd4 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java @@ -1,18 +1,22 @@ package com.genersoft.iot.vmp.gb28181.event; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.event.offline.OfflineEvent; -import com.genersoft.iot.vmp.gb28181.event.platformKeepaliveExpire.PlatformKeepaliveExpireEvent; -import com.genersoft.iot.vmp.gb28181.event.platformNotRegister.PlatformNotRegisterEvent; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.event.device.RequestTimeoutEvent; +import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEvent; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.media.zlm.event.ZLMOfflineEvent; import com.genersoft.iot.vmp.media.zlm.event.ZLMOnlineEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; -import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEvent; -import com.genersoft.iot.vmp.gb28181.event.online.OnlineEvent; + +import javax.sip.TimeoutEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * @description:Event事件通知推送器,支持推送在线事件、离线事件 @@ -25,40 +29,6 @@ public class EventPublisher { @Autowired private ApplicationEventPublisher applicationEventPublisher; - public void onlineEventPublish(Device device, String from) { - OnlineEvent onEvent = new OnlineEvent(this); - onEvent.setDevice(device); - onEvent.setFrom(from); - applicationEventPublisher.publishEvent(onEvent); - } - - public void outlineEventPublish(String deviceId, String from){ - OfflineEvent outEvent = new OfflineEvent(this); - outEvent.setDeviceId(deviceId); - outEvent.setFrom(from); - applicationEventPublisher.publishEvent(outEvent); - } - - /** - * 平台心跳到期事件 - * @param platformGbId - */ - public void platformKeepaliveExpireEventPublish(String platformGbId){ - PlatformKeepaliveExpireEvent platformNotRegisterEvent = new PlatformKeepaliveExpireEvent(this); - platformNotRegisterEvent.setPlatformGbID(platformGbId); - applicationEventPublisher.publishEvent(platformNotRegisterEvent); - } - - /** - * 平台未注册事件 - * @param platformGbId - */ - public void platformNotRegisterEventPublish(String platformGbId){ - PlatformNotRegisterEvent platformNotRegisterEvent = new PlatformNotRegisterEvent(this); - platformNotRegisterEvent.setPlatformGbID(platformGbId); - applicationEventPublisher.publishEvent(platformNotRegisterEvent); - } - /** * 设备报警事件 * @param deviceAlarm @@ -80,4 +50,69 @@ public class EventPublisher { outEvent.setMediaServerId(mediaServerId); applicationEventPublisher.publishEvent(outEvent); } + + + public void catalogEventPublish(String platformId, DeviceChannel deviceChannel, String type) { + List deviceChannelList = new ArrayList<>(); + deviceChannelList.add(deviceChannel); + catalogEventPublish(platformId, deviceChannelList, type); + } + + + public void requestTimeOut(TimeoutEvent timeoutEvent) { + RequestTimeoutEvent requestTimeoutEvent = new RequestTimeoutEvent(this); + requestTimeoutEvent.setTimeoutEvent(timeoutEvent); + applicationEventPublisher.publishEvent(requestTimeoutEvent); + } + + + /** + * + * @param platformId + * @param deviceChannels + * @param type + */ + public void catalogEventPublish(String platformId, List deviceChannels, String type) { + CatalogEvent outEvent = new CatalogEvent(this); + List channels = new ArrayList<>(); + if (deviceChannels.size() > 1) { + // 数据去重 + Set gbIdSet = new HashSet<>(); + for (DeviceChannel deviceChannel : deviceChannels) { + if (!gbIdSet.contains(deviceChannel.getChannelId())) { + gbIdSet.add(deviceChannel.getChannelId()); + channels.add(deviceChannel); + } + } + }else { + channels = deviceChannels; + } + outEvent.setDeviceChannels(channels); + outEvent.setType(type); + outEvent.setPlatformId(platformId); + applicationEventPublisher.publishEvent(outEvent); + } + + + 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 f341548e..efa4d424 100644 --- 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,18 +1,26 @@ package com.genersoft.iot.vmp.gb28181.event; +import com.genersoft.iot.vmp.gb28181.bean.DeviceNotFoundEvent; +import gov.nist.javax.sip.message.SIPRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import javax.sip.*; +import javax.sip.DialogTerminatedEvent; +import javax.sip.ResponseEvent; +import javax.sip.TimeoutEvent; +import javax.sip.TransactionTerminatedEvent; import javax.sip.header.CallIdHeader; import javax.sip.message.Response; -import java.util.Calendar; -import java.util.Date; +import java.time.Instant; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +/** + * @author lin + */ @Component public class SipSubscribe { @@ -22,48 +30,68 @@ public class SipSubscribe { private Map okSubscribes = new ConcurrentHashMap<>(); - private Map timeSubscribes = new ConcurrentHashMap<>(); + private Map okTimeSubscribes = new ConcurrentHashMap<>(); -// @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次 -// @Scheduled(fixedRate= 100 * 60 * 60 ) - @Scheduled(cron="0 0 * * * ?") //每小时执行一次, 每个整点 + private Map errorTimeSubscribes = new ConcurrentHashMap<>(); + + // @Scheduled(cron="*/5 * * * * ?") //每五秒执行一次 + // @Scheduled(fixedRate= 100 * 60 * 60 ) + @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次 public void execute(){ - logger.info("[定时任务] 清理过期的订阅信息"); - Calendar calendar = Calendar.getInstance(); - calendar.setTime(new Date()); - calendar.set(Calendar.HOUR, calendar.get(Calendar.HOUR) - 1); - for (String key : timeSubscribes.keySet()) { - if (timeSubscribes.get(key).before(calendar.getTime())){ - logger.info("[定时任务] 清理过期的订阅信息: {}", key); - errorSubscribes.remove(key); + 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); - timeSubscribes.remove(key); + okTimeSubscribes.remove(key); } } + for (String key : errorTimeSubscribes.keySet()) { + if (errorTimeSubscribes.get(key).isBefore(instant)){ + errorSubscribes.remove(key); + errorTimeSubscribes.remove(key); + } + } + 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); + public interface Event { void response(EventResult eventResult) ; + } + + /** + * + */ + public enum EventResultType{ + // 超时 + timeout, + // 回复 + response, + // 事务已结束 + transactionTerminated, + // 会话已结束 + dialogTerminated, + // 设备未找到 + deviceNotFoundEvent } public static class EventResult{ public int statusCode; - public String type; + public EventResultType type; public String msg; public String callId; - public Dialog dialog; public EventObject event; - public EventResult() { - } - public EventResult(EventObject event) { this.event = event; if (event instanceof ResponseEvent) { ResponseEvent responseEvent = (ResponseEvent)event; Response response = responseEvent.getResponse(); - this.dialog = responseEvent.getDialog(); - this.type = "response"; + this.type = EventResultType.response; if (response != null) { this.msg = response.getReasonPhrase(); this.statusCode = response.getStatusCode(); @@ -72,37 +100,47 @@ public class SipSubscribe { }else if (event instanceof TimeoutEvent) { TimeoutEvent timeoutEvent = (TimeoutEvent)event; - this.type = "timeout"; + this.type = EventResultType.timeout; this.msg = "消息超时未回复"; this.statusCode = -1024; - this.callId = timeoutEvent.getClientTransaction().getDialog().getCallId().getCallId(); - this.dialog = timeoutEvent.getClientTransaction().getDialog(); + if (timeoutEvent.isServerTransaction()) { + this.callId = ((SIPRequest)timeoutEvent.getServerTransaction().getRequest()).getCallIdHeader().getCallId(); + }else { + this.callId = ((SIPRequest)timeoutEvent.getClientTransaction().getRequest()).getCallIdHeader().getCallId(); + } }else if (event instanceof TransactionTerminatedEvent) { TransactionTerminatedEvent transactionTerminatedEvent = (TransactionTerminatedEvent)event; - this.type = "transactionTerminated"; + this.type = EventResultType.transactionTerminated; this.msg = "事务已结束"; this.statusCode = -1024; - this.callId = transactionTerminatedEvent.getClientTransaction().getDialog().getCallId().getCallId(); - this.dialog = transactionTerminatedEvent.getClientTransaction().getDialog(); + if (transactionTerminatedEvent.isServerTransaction()) { + this.callId = ((SIPRequest)transactionTerminatedEvent.getServerTransaction().getRequest()).getCallIdHeader().getCallId(); + }else { + this.callId = ((SIPRequest)transactionTerminatedEvent.getClientTransaction().getRequest()).getCallIdHeader().getCallId(); + } }else if (event instanceof DialogTerminatedEvent) { DialogTerminatedEvent dialogTerminatedEvent = (DialogTerminatedEvent)event; - this.type = "dialogTerminated"; + this.type = EventResultType.dialogTerminated; this.msg = "会话已结束"; this.statusCode = -1024; this.callId = dialogTerminatedEvent.getDialog().getCallId().getCallId(); - this.dialog = dialogTerminatedEvent.getDialog(); + }else if (event instanceof DeviceNotFoundEvent) { + this.type = EventResultType.deviceNotFoundEvent; + this.msg = "设备未找到"; + this.statusCode = -1024; + this.callId = ((DeviceNotFoundEvent) event).getCallId(); } } } public void addErrorSubscribe(String key, SipSubscribe.Event event) { errorSubscribes.put(key, event); - timeSubscribes.put(key, new Date()); + errorTimeSubscribes.put(key, Instant.now()); } public void addOkSubscribe(String key, SipSubscribe.Event event) { okSubscribes.put(key, event); - timeSubscribes.put(key, new Date()); + okTimeSubscribes.put(key, Instant.now()); } public SipSubscribe.Event getErrorSubscribe(String key) { @@ -110,8 +148,11 @@ public class SipSubscribe { } public void removeErrorSubscribe(String key) { + if(key == null){ + return; + } errorSubscribes.remove(key); - timeSubscribes.remove(key); + errorTimeSubscribes.remove(key); } public SipSubscribe.Event getOkSubscribe(String key) { @@ -119,8 +160,11 @@ public class SipSubscribe { } public void removeOkSubscribe(String key) { + if(key == null){ + return; + } okSubscribes.remove(key); - timeSubscribes.remove(key); + okTimeSubscribes.remove(key); } public int getErrorSubscribesSize(){ return errorSubscribes.size(); 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 2b563269..9ee64773 100644 --- 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 @@ -51,7 +51,6 @@ public class AlarmEventListener implements ApplicationListener { } // 移除已关闭的连接 it.remove(); - // e.printStackTrace(); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEvent.java new file mode 100644 index 00000000..c4d3baba --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEvent.java @@ -0,0 +1,25 @@ +package com.genersoft.iot.vmp.gb28181.event.device; + +import org.springframework.context.ApplicationEvent; + +import javax.sip.TimeoutEvent; + +/** + * @author lin + */ +public class RequestTimeoutEvent extends ApplicationEvent { + public RequestTimeoutEvent(Object source) { + super(source); + } + + + private TimeoutEvent timeoutEvent; + + public TimeoutEvent getTimeoutEvent() { + return timeoutEvent; + } + + public void setTimeoutEvent(TimeoutEvent timeoutEvent) { + this.timeoutEvent = timeoutEvent; + } +} 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 new file mode 100644 index 00000000..bffa4cb9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java @@ -0,0 +1,42 @@ +package com.genersoft.iot.vmp.gb28181.event.device; + +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.service.IDeviceService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import javax.sip.ClientTransaction; +import javax.sip.address.SipURI; +import javax.sip.header.CallIdHeader; +import javax.sip.header.ToHeader; +import javax.sip.message.Request; + +/** + * @author lin + */ +@Component +public class RequestTimeoutEventImpl implements ApplicationListener { + + @Autowired + private IDeviceService deviceService; + + @Override + public void onApplicationEvent(RequestTimeoutEvent event) { + ClientTransaction clientTransaction = event.getTimeoutEvent().getClientTransaction(); + if (clientTransaction != null) { + Request request = clientTransaction.getRequest(); + if (request != null) { + String host = ((SipURI) request.getRequestURI()).getHost(); + int port = ((SipURI) request.getRequestURI()).getPort(); + Device device = deviceService.getDeviceByHostAndPort(host, port); + if (device == null) { + return; + } + deviceService.offline(device.getDeviceId()); + } + + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java deleted file mode 100644 index 4d2b8ce4..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.offline; - -import com.genersoft.iot.vmp.conf.UserSetup; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.connection.Message; -import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; -import org.springframework.data.redis.listener.RedisMessageListenerContainer; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; - -/** - * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件 - * @author: swwheihei - * @date: 2020年5月6日 上午11:35:46 - */ -@Component -public class KeepaliveTimeoutListenerForPlatform extends KeyExpirationEventMessageListener { - - private Logger logger = LoggerFactory.getLogger(KeepaliveTimeoutListenerForPlatform.class); - - @Autowired - private EventPublisher publisher; - - @Autowired - private UserSetup userSetup; - - public KeepaliveTimeoutListenerForPlatform(RedisMessageListenerContainer listenerContainer) { - super(listenerContainer); - // 配置springboot默认Config为空,即不让应用去修改redis的默认配置,因为Redis服务出于安全会禁用CONFIG命令给远程用户使用 - setKeyspaceNotificationsConfigParameter(""); - } - - - /** - * 监听失效的key - * @param message - * @param pattern - */ - @Override - public void onMessage(Message message, byte[] pattern) { - // 获取失效的key - String expiredKey = message.toString(); - logger.debug(expiredKey); - // 平台心跳到期,需要重发, 判断是否已经多次未收到心跳回复, 多次未收到,则重新发起注册, 注册尝试多次未得到回复,则认为平台离线 - String PLATFORM_KEEPLIVEKEY_PREFIX = VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetup.getServerId() + "_"; - String PLATFORM_REGISTER_PREFIX = VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetup.getServerId() + "_"; - String KEEPLIVEKEY_PREFIX = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_"; - if (expiredKey.startsWith(PLATFORM_KEEPLIVEKEY_PREFIX)) { - String platformGBId = expiredKey.substring(PLATFORM_KEEPLIVEKEY_PREFIX.length(),expiredKey.length()); - - publisher.platformKeepaliveExpireEventPublish(platformGBId); - }else if (expiredKey.startsWith(PLATFORM_REGISTER_PREFIX)) { - String platformGBId = expiredKey.substring(PLATFORM_REGISTER_PREFIX.length(),expiredKey.length()); - - publisher.platformNotRegisterEventPublish(platformGBId); - }else{ - String deviceId = expiredKey.substring(KEEPLIVEKEY_PREFIX.length(),expiredKey.length()); - publisher.outlineEventPublish(deviceId, KEEPLIVEKEY_PREFIX); - } - - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java deleted file mode 100644 index 95a8ba98..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.offline; - -import com.genersoft.iot.vmp.conf.UserSetup; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.connection.Message; -import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; -import org.springframework.data.redis.listener.RedisMessageListenerContainer; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; - -/** - * @description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件 - * @author: swwheihei - * @date: 2020年5月6日 上午11:35:46 - */ -@Component -public class KeepliveTimeoutListener extends KeyExpirationEventMessageListener { - - private Logger logger = LoggerFactory.getLogger(KeepliveTimeoutListener.class); - - @Autowired - private EventPublisher publisher; - - @Autowired - private UserSetup userSetup; - - public KeepliveTimeoutListener(RedisMessageListenerContainer listenerContainer) { - super(listenerContainer); - // 配置springboot默认Config为空,即不让应用去修改redis的默认配置,因为Redis服务出于安全会禁用CONFIG命令给远程用户使用 - setKeyspaceNotificationsConfigParameter(""); - } - - /** - * 监听失效的key,key格式为keeplive_deviceId - * @param message - * @param pattern - */ - @Override - public void onMessage(Message message, byte[] pattern) { - // 获取失效的key - String expiredKey = message.toString(); - String KEEPLIVEKEY_PREFIX = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_"; - if(!expiredKey.startsWith(KEEPLIVEKEY_PREFIX)){ - logger.debug("收到redis过期监听,但开头不是"+KEEPLIVEKEY_PREFIX+",忽略"); - return; - } - - String deviceId = expiredKey.substring(KEEPLIVEKEY_PREFIX.length(),expiredKey.length()); - publisher.outlineEventPublish(deviceId, VideoManagerConstants.EVENT_OUTLINE_TIMEOUT); - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java deleted file mode 100644 index 9dfeffca..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.offline; - -import org.springframework.context.ApplicationEvent; - -/** - * @description: 离线事件类 - * @author: swwheihei - * @date: 2020年5月6日 上午11:33:13 - */ -public class OfflineEvent extends ApplicationEvent { - - /** - * - */ - private static final long serialVersionUID = 1L; - - public OfflineEvent(Object source) { - super(source); - } - - private String deviceId; - - private String from; - - public String getDeviceId() { - return deviceId; - } - - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getFrom() { - return from; - } - - public void setFrom(String from) { - this.from = from; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java deleted file mode 100644 index 0f5604c0..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/offline/OfflineEventListener.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.offline; - -import com.genersoft.iot.vmp.conf.UserSetup; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -/** - * @description: 离线事件监听器,监听到离线后,修改设备离在线状态。 设备离线有两个来源: - * 1、设备主动注销,发送注销指令,{@link com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.RegisterRequestProcessor} - * 2、设备未知原因离线,心跳超时,{@link com.genersoft.iot.vmp.gb28181.event.offline.OfflineEventListener} - * @author: swwheihei - * @date: 2020年5月6日 下午1:51:23 - */ -@Component -public class OfflineEventListener implements ApplicationListener { - - private final static Logger logger = LoggerFactory.getLogger(OfflineEventListener.class); - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private RedisUtil redis; - - @Autowired - private UserSetup userSetup; - - @Override - public void onApplicationEvent(OfflineEvent event) { - - if (logger.isDebugEnabled()) { - logger.debug("设备离线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom()); - } - - String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + event.getDeviceId(); - - switch (event.getFrom()) { - // 心跳超时触发的离线事件,说明redis中已删除,无需处理 - case VideoManagerConstants.EVENT_OUTLINE_TIMEOUT: - break; - // 设备主动注销触发的离线事件,需要删除redis中的超时监听 - case VideoManagerConstants.EVENT_OUTLINE_UNREGISTER: - redis.del(key); - break; - default: - boolean exist = redis.hasKey(key); - if (exist) { - redis.del(key); - } - } - - // 处理离线监听 - storager.outline(event.getDeviceId()); - - // TODO 离线取消订阅 - - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java deleted file mode 100644 index 73d7f1f7..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEvent.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.online; - -import com.genersoft.iot.vmp.gb28181.bean.Device; -import org.springframework.context.ApplicationEvent; - -/** - * @description: 在线事件类 - * @author: swwheihei - * @date: 2020年5月6日 上午11:32:56 - */ -public class OnlineEvent extends ApplicationEvent { - - /** - * - */ - private static final long serialVersionUID = 1L; - - public OnlineEvent(Object source) { - super(source); - } - - private Device device; - - private String from; - - public Device getDevice() { - return device; - } - - public void setDevice(Device device) { - this.device = device; - } - - public String getFrom() { - return from; - } - - public void setFrom(String from) { - this.from = from; - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java deleted file mode 100644 index a62c76d5..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/online/OnlineEventListener.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.online; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.storager.dao.dto.User; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; - -import java.text.SimpleDateFormat; - -/** - * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源: - * 1、设备主动注销,发送注销指令 - * 2、设备未知原因离线,心跳超时 - * @author: swwheihei - * @date: 2020年5月6日 下午1:51:23 - */ -@Component -public class OnlineEventListener implements ApplicationListener { - - private final static Logger logger = LoggerFactory.getLogger(OnlineEventListener.class); - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private RedisUtil redis; - - @Autowired - private SipConfig sipConfig; - - @Autowired - private UserSetup userSetup; - - private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - @Override - public void onApplicationEvent(OnlineEvent event) { - - if (logger.isDebugEnabled()) { - logger.debug("设备上线事件触发,deviceId:" + event.getDevice().getDeviceId() + ",from:" + event.getFrom()); - } - Device device = event.getDevice(); - String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + event.getDevice().getDeviceId(); - - switch (event.getFrom()) { - // 注册时触发的在线事件,先在redis中增加超时超时监听 - case VideoManagerConstants.EVENT_ONLINE_REGISTER: - // 超时时间 - redis.set(key, event.getDevice().getDeviceId(), sipConfig.getKeepaliveTimeOut()); - device.setRegisterTime(format.format(System.currentTimeMillis())); - break; - // 设备主动发送心跳触发的在线事件 - case VideoManagerConstants.EVENT_ONLINE_KEEPLIVE: - boolean exist = redis.hasKey(key); - // 先判断是否还存在,当设备先心跳超时后又发送心跳时,redis没有监听,需要增加 - if (!exist) { - redis.set(key, event.getDevice().getDeviceId(), sipConfig.getKeepaliveTimeOut()); - } else { - redis.expire(key, sipConfig.getKeepaliveTimeOut()); - } - device.setKeepaliveTime(format.format(System.currentTimeMillis())); - break; - // 设备主动发送消息触发的在线事件 - case VideoManagerConstants.EVENT_ONLINE_MESSAGE: - - break; - } - - device.setOnline(1); - // 处理上线监听 - storager.updateDevice(device); - - // TODO 上线添加订阅 - - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEvent.java deleted file mode 100644 index 1e9a2c4b..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.platformKeepaliveExpire; - -import org.springframework.context.ApplicationEvent; - -/** - * 平台心跳超时事件 - */ -public class PlatformKeepaliveExpireEvent extends ApplicationEvent { - - /** - * Add default serial version ID - */ - private static final long serialVersionUID = 1L; - - private String platformGbID; - - public PlatformKeepaliveExpireEvent(Object source) { - super(source); - } - - public String getPlatformGbID() { - return platformGbID; - } - - public void setPlatformGbID(String platformGbID) { - this.platformGbID = platformGbID; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java deleted file mode 100644 index 00926574..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.platformKeepaliveExpire; - -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; - -import javax.sip.ResponseEvent; -import javax.sip.message.Response; - -/** - * @description: 平台心跳超时事件 - * @author: panll - * @date: 2020年11月5日 10:00 - */ -@Component -public class PlatformKeepaliveExpireEventLister implements ApplicationListener { - - - private final static Logger logger = LoggerFactory.getLogger(PlatformKeepaliveExpireEventLister.class); - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired - private ISIPCommanderForPlatform sipCommanderForPlatform; - - @Autowired - private SipSubscribe sipSubscribe; - - @Autowired - private EventPublisher publisher; - - @Override - public void onApplicationEvent(@NotNull PlatformKeepaliveExpireEvent event) { - - if (logger.isDebugEnabled()) { - logger.debug("平台心跳到期事件事件触发,平台国标ID:" + event.getPlatformGbID()); - } - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformGbID()); - ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(event.getPlatformGbID()); - if (parentPlatformCatch == null) { - return; - } - if (parentPlatform == null) { - logger.debug("平台心跳到期事件事件触发,但平台已经删除!!! 平台国标ID:" + event.getPlatformGbID()); - return; - } - parentPlatformCatch.setParentPlatform(parentPlatform); - // 发送心跳 - if (parentPlatformCatch.getKeepAliveReply() >= 3) { - // 有3次未收到心跳回复, 设置平台状态为离线, 开始重新注册 - logger.warn("有3次未收到心跳回复,标记设置平台状态为离线, 并重新注册 平台国标ID:" + event.getPlatformGbID()); - storager.updateParentPlatformStatus(event.getPlatformGbID(), false); - publisher.platformNotRegisterEventPublish(event.getPlatformGbID()); - parentPlatformCatch.setKeepAliveReply(0); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - }else { - // 再次发送心跳 - String callId = sipCommanderForPlatform.keepalive(parentPlatform); - - parentPlatformCatch.setKeepAliveReply( parentPlatformCatch.getKeepAliveReply() + 1); - // 存储心跳信息, 并设置状态为未回复, 如果多次过期仍未收到回复,则认为上级平台已经离线 - redisCatchStorage.updatePlatformKeepalive(parentPlatform); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - - sipSubscribe.addOkSubscribe(callId, (SipSubscribe.EventResult eventResult) ->{ - if (eventResult.statusCode == Response.OK) { - // 收到心跳响应信息, - parentPlatformCatch.setKeepAliveReply(0); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - } - } ); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEvent.java deleted file mode 100644 index c9369754..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEvent.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.platformNotRegister; - -import org.springframework.context.ApplicationEvent; - -public class PlatformNotRegisterEvent extends ApplicationEvent { - - /** - * Add default serial version ID - */ - private static final long serialVersionUID = 1L; - - private String platformGbID; - - public PlatformNotRegisterEvent(Object source) { - super(source); - } - - public String getPlatformGbID() { - return platformGbID; - } - - public void setPlatformGbID(String platformGbID) { - this.platformGbID = platformGbID; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java deleted file mode 100644 index 2ab2b239..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.event.platformNotRegister; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; -import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; -import com.genersoft.iot.vmp.service.IMediaServerService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; - -import java.util.*; - -/** - * @description: 平台未注册事件,来源有二: - * 1、平台新添加 - * 2、平台心跳超时 - * @author: panll - * @date: 2020年11月24日 10:00 - */ -@Component -public class PlatformNotRegisterEventLister implements ApplicationListener { - - private final static Logger logger = LoggerFactory.getLogger(PlatformNotRegisterEventLister.class); - - @Autowired - private IVideoManagerStorager storager; - @Autowired - private IRedisCatchStorage redisCatchStorage; - @Autowired - private IMediaServerService mediaServerService; - - @Autowired - private SIPCommanderFroPlatform sipCommanderFroPlatform; - - @Autowired - private ZLMRTPServerFactory zlmrtpServerFactory; - - @Autowired - private SipConfig config; - - // @Autowired - // private RedisUtil redis; - - @Override - public void onApplicationEvent(PlatformNotRegisterEvent event) { - - logger.info("[ 平台未注册事件 ]平台国标ID:" + event.getPlatformGbID()); - - ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformGbID()); - if (parentPlatform == null) { - logger.info("[ 平台未注册事件 ] 平台已经删除!!! 平台国标ID:" + event.getPlatformGbID()); - return; - } - // 查询是否有推流, 如果有则都停止 - List sendRtpItems = redisCatchStorage.querySendRTPServer(event.getPlatformGbID()); - logger.info("[ 平台未注册事件 ] 停止[ {} ]的所有推流size", sendRtpItems.size()); - if (sendRtpItems != null && sendRtpItems.size() > 0) { - logger.info("[ 平台未注册事件 ] 停止[ {} ]的所有推流", event.getPlatformGbID()); - StringBuilder app = new StringBuilder(); - StringBuilder stream = new StringBuilder(); - for (SendRtpItem sendRtpItem : sendRtpItems) { - if (app.length() != 0) { - app.append(","); - } - app.append(sendRtpItem.getApp()); - if (stream.length() != 0) { - stream.append(","); - } - stream.append(sendRtpItem.getStreamId()); - redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItem.getChannelId()); - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - Map param = new HashMap<>(); - param.put("vhost", "__defaultVhost__"); - param.put("app", app.toString()); - param.put("stream", stream.toString()); - zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); - } - - - } - - Timer timer = new Timer(); - SipSubscribe.Event okEvent = (responseEvent)->{ - timer.cancel(); - }; - logger.info("[平台注册]平台国标ID:" + event.getPlatformGbID()); - sipCommanderFroPlatform.register(parentPlatform, null, okEvent); - // 设置注册失败则每隔15秒发起一次注册 - timer.schedule(new TimerTask() { - @Override - public void run() { - logger.info("[平台注册]再次向平台注册,平台国标ID:" + event.getPlatformGbID()); - sipCommanderFroPlatform.register(parentPlatform, null, okEvent); - } - }, config.getRegisterTimeInterval()* 1000, config.getRegisterTimeInterval()* 1000);//十五秒后再次发起注册 - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEvent.java new file mode 100644 index 00000000..cfd2985c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEvent.java @@ -0,0 +1,32 @@ +package com.genersoft.iot.vmp.gb28181.event.record; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; +import org.springframework.context.ApplicationEvent; + +/** + * @description: 录像查询结束时间 + * @author: pan + * @data: 2022-02-23 + */ + +public class RecordEndEvent extends ApplicationEvent { + /** + * + */ + private static final long serialVersionUID = 1L; + + public RecordEndEvent(Object source) { + super(source); + } + + private RecordInfo recordInfo; + + public RecordInfo getRecordInfo() { + return recordInfo; + } + + public void setRecordInfo(RecordInfo recordInfo) { + this.recordInfo = recordInfo; + } +} 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 new file mode 100644 index 00000000..92a43517 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.gb28181.event.record; + +import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @description: 录像查询结束事件 + * @author: pan + * @data: 2022-02-23 + */ + +@Component +public class RecordEndEventListener implements ApplicationListener { + + private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class); + + public interface RecordEndEventHandler{ + void handler(RecordInfo recordInfo); + } + + private Map handlerMap = new ConcurrentHashMap<>(); + + @Override + public void onApplicationEvent(RecordEndEvent event) { + logger.info("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}条", event.getRecordInfo().getDeviceId(), + event.getRecordInfo().getChannelId(), event.getRecordInfo().getSumNum() ); + if (handlerMap.size() > 0) { + for (RecordEndEventHandler recordEndEventHandler : handlerMap.values()) { + recordEndEventHandler.handler(event.getRecordInfo()); + } + } + handlerMap.clear(); + } + + public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) { + handlerMap.put(device + channelId, recordEndEventHandler); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java new file mode 100644 index 00000000..5c064f75 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEvent.java @@ -0,0 +1,85 @@ +package com.genersoft.iot.vmp.gb28181.event.subscribe.catalog; + +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +import org.springframework.context.ApplicationEvent; + +import java.util.List; + +public class CatalogEvent extends ApplicationEvent { + public CatalogEvent(Object source) { + super(source); + } + + /** + * 上线 + */ + public static final String ON = "ON"; + + /** + * 离线 + */ + public static final String OFF = "OFF"; + + /** + * 视频丢失 + */ + public static final String VLOST = "VLOST"; + + /** + * 故障 + */ + public static final String DEFECT = "DEFECT"; + + /** + * 增加 + */ + public static final String ADD = "ADD"; + + /** + * 删除 + */ + public static final String DEL = "DEL"; + + /** + * 更新 + */ + public static final String UPDATE = "UPDATE"; + + private List deviceChannels; + private List gbStreams; + private String type; + private String platformId; + + public List getDeviceChannels() { + return deviceChannels; + } + + 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 new file mode 100644 index 00000000..be73ebd9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/subscribe/catalog/CatalogEventLister.java @@ -0,0 +1,194 @@ +package com.genersoft.iot.vmp.gb28181.event.subscribe.catalog; + +import com.genersoft.iot.vmp.common.VideoManagerConstants; +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.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; +import com.genersoft.iot.vmp.service.IGbStreamService; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.*; + +/** + * catalog事件 + */ +@Component +public class CatalogEventLister implements ApplicationListener { + + private final static Logger logger = LoggerFactory.getLogger(CatalogEventLister.class); + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommanderFroPlatform sipCommanderFroPlatform; + + @Autowired + private IGbStreamService gbStreamService; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Override + public void onApplicationEvent(CatalogEvent event) { + SubscribeInfo subscribe = null; + ParentPlatform parentPlatform = null; + + Map> parentPlatformMap = new HashMap<>(); + if (!ObjectUtils.isEmpty(event.getPlatformId())) { + subscribe = subscribeHolder.getCatalogSubscribe(event.getPlatformId()); + if (subscribe == null) { + return; + } + parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformId()); + if (parentPlatform != null && !parentPlatform.isStatus()) { + 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); + } + } + }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); + } + } + } + } + switch (event.getType()) { + case CatalogEvent.ON: + case CatalogEvent.OFF: + case CatalogEvent.DEL: + + if (parentPlatform != null || subscribe != null) { + List deviceChannelList = new ArrayList<>(); + if (event.getDeviceChannels() != null) { + deviceChannelList.addAll(event.getDeviceChannels()); + } + if (event.getGbStreams() != null && event.getGbStreams().size() > 0){ + for (GbStream gbStream : event.getGbStreams()) { + DeviceChannel deviceChannelByStream = gbStreamService.getDeviceChannelListByStream(gbStream, gbStream.getCatalogId(), parentPlatform); + deviceChannelList.add(deviceChannelByStream); + } + } + if (deviceChannelList.size() > 0) { + logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size()); + try { + sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, deviceChannelList, subscribe, null); + } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | + IllegalAccessException e) { + logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + } + } + }else if (parentPlatformMap.keySet().size() > 0) { + for (String gbId : parentPlatformMap.keySet()) { + List parentPlatforms = parentPlatformMap.get(gbId); + if (parentPlatforms != null && parentPlatforms.size() > 0) { + for (ParentPlatform 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); + deviceChannelList.add(deviceChannel); + try { + sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null); + } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | + IllegalAccessException e) { + logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + } + } + } + } + } + break; + case CatalogEvent.VLOST: + break; + case CatalogEvent.DEFECT: + 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 (event.getGbStreams() != null && event.getGbStreams().size() > 0){ + for (GbStream gbStream : event.getGbStreams()) { + deviceChannelList.add( + gbStreamService.getDeviceChannelListByStreamWithStatus(gbStream, gbStream.getCatalogId(), parentPlatform)); + } + } + if (deviceChannelList.size() > 0) { + logger.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), event.getPlatformId(), deviceChannelList.size()); + try { + sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), parentPlatform, deviceChannelList, subscribe, null); + } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | + IllegalAccessException e) { + logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + } + } + }else if (parentPlatformMap.keySet().size() > 0) { + for (String gbId : parentPlatformMap.keySet()) { + List parentPlatforms = parentPlatformMap.get(gbId); + if (parentPlatforms != null && parentPlatforms.size() > 0) { + for (ParentPlatform 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 = storager.queryChannelInParentPlatform(platform.getServerGBId(), 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); + } + try { + sipCommanderFroPlatform.sendNotifyForCatalogAddOrUpdate(event.getType(), platform, deviceChannelList, subscribeInfo, null); + } catch (InvalidArgumentException | ParseException | NoSuchFieldException | + SipException | IllegalAccessException e) { + logger.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); + } + } + } + } + } + break; + default: + break; + } + } +} + \ No newline at end of file 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 new file mode 100644 index 00000000..38cdf7c4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java @@ -0,0 +1,146 @@ +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +@Component +public class CatalogDataCatch { + + public static Map data = new ConcurrentHashMap<>(); + + @Autowired + private IVideoManagerStorage storager; + + public void addReady(Device device, int sn ) { + CatalogData catalogData = data.get(device.getDeviceId()); + if (catalogData == null || catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) { + catalogData = new CatalogData(); + catalogData.setChannelList(Collections.synchronizedList(new ArrayList<>())); + catalogData.setDevice(device); + catalogData.setSn(sn); + catalogData.setStatus(CatalogData.CatalogDataStatus.ready); + catalogData.setLastTime(Instant.now()); + data.put(device.getDeviceId(), catalogData); + } + } + + public void put(String deviceId, int sn, int total, Device device, List deviceChannelList) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + catalogData = new CatalogData(); + catalogData.setSn(sn); + catalogData.setTotal(total); + catalogData.setDevice(device); + catalogData.setChannelList(deviceChannelList); + catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); + catalogData.setLastTime(Instant.now()); + data.put(deviceId, catalogData); + }else { + // 同一个设备的通道同步请求只考虑一个,其他的直接忽略 + if (catalogData.getSn() != sn) { + return; + } + catalogData.setTotal(total); + catalogData.setDevice(device); + catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); + catalogData.getChannelList().addAll(deviceChannelList); + catalogData.setLastTime(Instant.now()); + } + } + + public List get(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return null; + } + return catalogData.getChannelList(); + } + + public int getTotal(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return 0; + } + return catalogData.getTotal(); + } + + public SyncStatus getSyncStatus(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return null; + } + SyncStatus syncStatus = new SyncStatus(); + syncStatus.setCurrent(catalogData.getChannelList().size()); + syncStatus.setTotal(catalogData.getTotal()); + syncStatus.setErrorMsg(catalogData.getErrorMsg()); + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) { + syncStatus.setSyncIng(false); + }else { + syncStatus.setSyncIng(true); + } + return syncStatus; + } + + public boolean isSyncRunning(String deviceId) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return false; + } + return !catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end); + } + + @Scheduled(fixedRate = 5 * 1000) //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时 + private void timerTask(){ + Set keys = data.keySet(); + + Instant instantBefore5S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(5)); + Instant instantBefore30S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(30)); + + for (String deviceId : keys) { + CatalogData catalogData = data.get(deviceId); + if ( catalogData.getLastTime().isBefore(instantBefore5S)) { + // 超过五秒收不到消息任务超时, 只更新这一部分数据, 收到数据与声明的总数一致,则重置通道数据,数据不全则只对收到的数据做更新操作 + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) { + if (catalogData.getTotal() == catalogData.getChannelList().size()) { + storager.resetChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList()); + }else { + storager.updateChannels(catalogData.getDevice().getDeviceId(), catalogData.getChannelList()); + } + String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "条"; + catalogData.setErrorMsg(errorMsg); + if (catalogData.getTotal() != catalogData.getChannelList().size()) { + + } + }else if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready)) { + String errorMsg = "同步失败,等待回复超时"; + catalogData.setErrorMsg(errorMsg); + } + catalogData.setStatus(CatalogData.CatalogDataStatus.end); + } + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end) && catalogData.getLastTime().isBefore(instantBefore30S)) { // 超过三十秒,如果标记为end则删除 + data.remove(deviceId); + } + } + } + + + public void setChannelSyncEnd(String deviceId, String errorMsg) { + CatalogData catalogData = data.get(deviceId); + if (catalogData == null) { + return; + } + catalogData.setStatus(CatalogData.CatalogDataStatus.end); + catalogData.setErrorMsg(errorMsg); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java new file mode 100644 index 00000000..1d2b34b0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/RecordDataCatch.java @@ -0,0 +1,87 @@ +package com.genersoft.iot.vmp.gb28181.session; + +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.vmanager.bean.WVPResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * @author lin + */ +@Component +public class RecordDataCatch { + + public static Map data = new ConcurrentHashMap<>(); + + @Autowired + private DeferredResultHolder deferredResultHolder; + + + public int put(String deviceId, String sn, int sumNum, List recordItems) { + String key = deviceId + sn; + RecordInfo recordInfo = data.get(key); + if (recordInfo == null) { + recordInfo = new RecordInfo(); + recordInfo.setDeviceId(deviceId); + recordInfo.setSn(sn.trim()); + recordInfo.setSumNum(sumNum); + recordInfo.setRecordList(Collections.synchronizedList(new ArrayList<>())); + recordInfo.setLastTime(Instant.now()); + recordInfo.getRecordList().addAll(recordItems); + data.put(key, recordInfo); + }else { + // 同一个设备的通道同步请求只考虑一个,其他的直接忽略 + if (!Objects.equals(sn.trim(), recordInfo.getSn())) { + return 0; + } + recordInfo.getRecordList().addAll(recordItems); + recordInfo.setLastTime(Instant.now()); + } + return recordInfo.getRecordList().size(); + } + + @Scheduled(fixedRate = 5 * 1000) //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时 + private void timerTask(){ + Set keys = data.keySet(); + // 获取五秒前的时刻 + Instant instantBefore5S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(5)); + for (String key : keys) { + RecordInfo recordInfo = data.get(key); + // 超过五秒收不到消息任务超时, 只更新这一部分数据 + if ( recordInfo.getLastTime().isBefore(instantBefore5S)) { + // 处理录像数据, 返回给前端 + String msgKey = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getSn(); + + // 对数据进行排序 + Collections.sort(recordInfo.getRecordList()); + + RequestMessage msg = new RequestMessage(); + msg.setKey(msgKey); + msg.setData(recordInfo); + deferredResultHolder.invokeAllResult(msg); + data.remove(key); + } + } + } + + public boolean isComplete(String deviceId, String sn) { + RecordInfo recordInfo = data.get(deviceId + sn); + return recordInfo != null && recordInfo.getRecordList().size() == recordInfo.getSumNum(); + } + + public RecordInfo getRecordInfo(String deviceId, String sn) { + return data.get(deviceId + sn); + } + + public void remove(String deviceId, String sn) { + data.remove(deviceId + sn); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java index e96e6a5b..cc303c8b 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SsrcConfig.java @@ -1,27 +1,35 @@ package com.genersoft.iot.vmp.gb28181.session; import com.genersoft.iot.vmp.utils.ConfigConst; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.Set; +@Schema(description = "ssrc信息") public class SsrcConfig { /** * zlm流媒体服务器Id */ + @Schema(description = "流媒体服务器Id") private String mediaServerId; + @Schema(description = "SSRC前缀") private String ssrcPrefix; + /** * zlm流媒体服务器已用会话句柄 */ + @Schema(description = "zlm流媒体服务器已用会话句柄") private List isUsed; + /** * zlm流媒体服务器可用会话句柄 */ + @Schema(description = "zlm流媒体服务器可用会话句柄") private List notUsed; public SsrcConfig() { @@ -81,7 +89,6 @@ public class SsrcConfig { isUsed.remove(sn); notUsed.add(sn); }catch (NullPointerException e){ - System.out.printf("11111"); } } @@ -137,4 +144,7 @@ public class SsrcConfig { this.notUsed = notUsed; } + public boolean checkSsrc(String ssrcInResponse) { + return !isUsed.contains(ssrcInResponse); + } } 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 index d41b04da..d9b9cc5a 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/VideoStreamSessionManager.java @@ -1,20 +1,18 @@ 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.SipTransactionInfo; +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; +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.stereotype.Component; +import org.springframework.util.ObjectUtils; + import java.util.ArrayList; import java.util.List; -import javax.sip.ClientTransaction; -import javax.sip.Dialog; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; -import com.genersoft.iot.vmp.utils.SerializeUtils; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; -import gov.nist.javax.sip.stack.SIPDialog; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - /** * @description:视频流session管理器,管理视频预览、预览回放的通信句柄 * @author: swwheihei @@ -24,85 +22,118 @@ import org.springframework.stereotype.Component; public class VideoStreamSessionManager { @Autowired - private RedisUtil redisUtil; + private UserSetting userSetting; - @Autowired - private UserSetup userSetup; + public enum SessionType { + play, + playback, + download + } - public void put(String deviceId, String channelId ,String ssrc, String streamId, String mediaServerId, ClientTransaction transaction){ + /** + * 添加一个点播/回放的事务信息 + * 后续可以通过流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, SessionType type){ SsrcTransaction ssrcTransaction = new SsrcTransaction(); ssrcTransaction.setDeviceId(deviceId); ssrcTransaction.setChannelId(channelId); - ssrcTransaction.setStreamId(streamId); - byte[] transactionByteArray = SerializeUtils.serialize(transaction); - ssrcTransaction.setTransaction(transactionByteArray); + ssrcTransaction.setStream(stream); + ssrcTransaction.setSipTransactionInfo(new SipTransactionInfo(response)); + ssrcTransaction.setCallId(callId); ssrcTransaction.setSsrc(ssrc); ssrcTransaction.setMediaServerId(mediaServerId); + ssrcTransaction.setType(type); - redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" + deviceId + "_" + channelId, ssrcTransaction); + RedisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + + "_" + deviceId + "_" + channelId + "_" + callId + "_" + stream, ssrcTransaction); } - public void put(String deviceId, String channelId , Dialog dialog){ - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId); - if (ssrcTransaction != null) { - byte[] dialogByteArray = SerializeUtils.serialize(dialog); - ssrcTransaction.setDialog(dialogByteArray); + public SsrcTransaction getSsrcTransaction(String deviceId, String channelId, String callId, String stream){ + + if (ObjectUtils.isEmpty(deviceId)) { + deviceId ="*"; } - redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" + deviceId + "_" + channelId, ssrcTransaction); + 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(key); + if (scanResult.size() == 0) { + return null; + } + return (SsrcTransaction)RedisUtil.get((String) scanResult.get(0)); } - - public ClientTransaction getTransaction(String deviceId, String channelId){ - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId); - if (ssrcTransaction == null) return null; - byte[] transactionByteArray = ssrcTransaction.getTransaction(); - ClientTransaction clientTransaction = (ClientTransaction)SerializeUtils.deSerialize(transactionByteArray); - return clientTransaction; + 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(key); + if (scanResult.size() == 0) { + return null; + } + List result = new ArrayList<>(); + for (Object keyObj : scanResult) { + result.add((SsrcTransaction)RedisUtil.get((String) keyObj)); + } + return result; } - public SIPDialog getDialog(String deviceId, String channelId){ - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId); - if (ssrcTransaction == null) return null; - byte[] dialogByteArray = ssrcTransaction.getDialog(); - if (dialogByteArray == null) return null; - SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray); - return dialog; - } - - public SsrcTransaction getSsrcTransaction(String deviceId, String channelId){ - SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" + deviceId + "_" + channelId); - return ssrcTransaction; - } - - public String getStreamId(String deviceId, String channelId){ - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId); - if (ssrcTransaction == null) return null; - return ssrcTransaction.getStreamId(); - } - public String getMediaServerId(String deviceId, String channelId){ - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId); - if (ssrcTransaction == null) return null; + 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){ - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId); - if (ssrcTransaction == null) return null; + 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) { - SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId); - if (ssrcTransaction == null) return; - redisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetup.getServerId() + "_" + deviceId + "_" + channelId); + public void remove(String deviceId, String channelId, String stream) { + SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId, null, stream); + if (ssrcTransaction == null) { + return; + } + RedisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + userSetting.getServerId() + "_" + + deviceId + "_" + channelId + "_" + ssrcTransaction.getCallId() + "_" + ssrcTransaction.getStream()); } + public List getAllSsrc() { - List ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetup.getServerId() + "_" )); + List ssrcTransactionKeys = RedisUtil.scan(String.format("%s_*_*_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX+ userSetting.getServerId())); List result= new ArrayList<>(); for (int i = 0; i < ssrcTransactionKeys.size(); i++) { String key = (String)ssrcTransactionKeys.get(i); - SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(key); + SsrcTransaction ssrcTransaction = (SsrcTransaction)RedisUtil.get(key); result.add(ssrcTransaction); } return result; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/ISubscribeTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/ISubscribeTask.java new file mode 100644 index 00000000..a4e711d9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/ISubscribeTask.java @@ -0,0 +1,10 @@ +package com.genersoft.iot.vmp.gb28181.task; + +import javax.sip.DialogState; + +/** + * @author lin + */ +public interface ISubscribeTask extends Runnable{ + void stop(); +} 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 new file mode 100644 index 00000000..451c589c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java @@ -0,0 +1,95 @@ +package com.genersoft.iot.vmp.gb28181.task; + +import com.alibaba.fastjson2.JSONObject; +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.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.IPlatformService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 系统启动时控制设备 + * @author lin + */ +@Component +@Order(value=4) +public class SipRunner implements CommandLineRunner { + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private UserSetting userSetting; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private ZLMRESTfulUtils zlmresTfulUtils; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IPlatformService platformService; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Override + public void run(String... args) throws Exception { + List deviceList = deviceService.getAllOnlineDevice(); + + for (Device device : deviceList) { + if (deviceService.expire(device)){ + deviceService.offline(device.getDeviceId()); + }else { + deviceService.online(device); + } + } + // 重置cseq计数 + redisCatchStorage.resetAllCSEQ(); + // 清理redis + // 查找国标推流 + List sendRtpItems = redisCatchStorage.queryAllSendRTPServer(); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStreamId()); + if (mediaServerItem != null) { + Map param = new HashMap<>(); + param.put("vhost","__defaultVhost__"); + param.put("app",sendRtpItem.getApp()); + param.put("stream",sendRtpItem.getStreamId()); + param.put("ssrc",sendRtpItem.getSsrc()); + JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param); + if (jsonObject != null && jsonObject.getInteger("code") == 0) { + ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId()); + if (platform != null) { + commanderForPlatform.streamByeCmd(platform, sendRtpItem.getCallId()); + } + } + } + } + } + } +} 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 new file mode 100644 index 00000000..39dff931 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/CatalogSubscribeTask.java @@ -0,0 +1,105 @@ +package com.genersoft.iot.vmp.gb28181.task.impl; + +import com.genersoft.iot.vmp.conf.DynamicTask; +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 org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import javax.sip.*; +import javax.sip.header.ToHeader; +import java.text.ParseException; +import java.util.Timer; +import java.util.TimerTask; + +/** + * 目录订阅任务 + * @author lin + */ +public class CatalogSubscribeTask implements ISubscribeTask { + private final Logger logger = LoggerFactory.getLogger(CatalogSubscribeTask.class); + private Device device; + private final ISIPCommander sipCommander; + private SIPRequest request; + + private DynamicTask dynamicTask; + + private String taskKey = "catalog-subscribe-timeout"; + + + public CatalogSubscribeTask(Device device, ISIPCommander sipCommander, DynamicTask dynamicTask) { + this.device = device; + this.sipCommander = sipCommander; + this.dynamicTask = dynamicTask; + } + + @Override + public void run() { + if (dynamicTask.get(taskKey) != null) { + dynamicTask.stop(taskKey); + } + SIPRequest sipRequest = null; + try { + sipRequest = sipCommander.catalogSubscribe(device, request, eventResult -> { + ResponseEvent event = (ResponseEvent) eventResult.event; + // 成功 + logger.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失败"); + this.request = null; + } + },eventResult -> { + this.request = null; + // 失败 + logger.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + dynamicTask.startDelay(taskKey, CatalogSubscribeTask.this, 2000); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 目录订阅: {}", e.getMessage()); + + } + if (sipRequest != null) { + this.request = sipRequest; + } + } + + @Override + public void stop() { + /** + * dialog 的各个状态 + * EARLY-> Early state状态-初始请求发送以后,收到了一个临时响应消息 + * CONFIRMED-> Confirmed Dialog状态-已确认 + * COMPLETED-> Completed Dialog状态-已完成 + * TERMINATED-> Terminated Dialog状态-终止 + */ + logger.info("取消目录订阅时dialog状态为{}", DialogState.CONFIRMED); + if (dynamicTask.get(taskKey) != null) { + dynamicTask.stop(taskKey); + } + device.setSubscribeCycleForCatalog(0); + try { + sipCommander.catalogSubscribe(device, request, eventResult -> { + ResponseEvent event = (ResponseEvent) eventResult.event; + if (event.getResponse().getRawContent() != null) { + // 成功 + logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId()); + }else { + // 成功 + logger.info("[取消目录订阅订阅]成功: {}", device.getDeviceId()); + } + },eventResult -> { + // 失败 + logger.warn("[取消目录订阅订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 取消目录订阅订阅: {}", e.getMessage()); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java new file mode 100644 index 00000000..2e792c1c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeHandlerTask.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.gb28181.task.impl; + +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.service.IPlatformService; +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.utils.SpringBeanFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; + +import javax.sip.DialogState; +import java.util.List; + +/** + * 向已经订阅(移动位置)的上级发送MobilePosition消息 + * @author lin + */ +public class MobilePositionSubscribeHandlerTask implements ISubscribeTask { + + + private IPlatformService platformService; + private String platformId; + + + public MobilePositionSubscribeHandlerTask(String platformId) { + this.platformService = SpringBeanFactory.getBean("platformServiceImpl"); + this.platformId = platformId; + } + + @Override + public void run() { + platformService.sendNotifyMobilePosition(this.platformId); + } + + @Override + public void stop() { + + } +} 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 new file mode 100644 index 00000000..0abd3cae --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/task/impl/MobilePositionSubscribeTask.java @@ -0,0 +1,103 @@ +package com.genersoft.iot.vmp.gb28181.task.impl; + +import com.genersoft.iot.vmp.conf.DynamicTask; +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 gov.nist.javax.sip.message.SIPResponse; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; + +import javax.sip.*; +import javax.sip.header.ToHeader; +import java.text.ParseException; +import java.util.Timer; +import java.util.TimerTask; + +/** + * 移动位置订阅的定时更新 + * @author lin + */ +public class MobilePositionSubscribeTask implements ISubscribeTask { + private final Logger logger = LoggerFactory.getLogger(MobilePositionSubscribeTask.class); + private Device device; + private ISIPCommander sipCommander; + + private SIPRequest request; + private DynamicTask dynamicTask; + private String taskKey = "mobile-position-subscribe-timeout"; + + public MobilePositionSubscribeTask(Device device, ISIPCommander sipCommander, DynamicTask dynamicTask) { + this.device = device; + this.sipCommander = sipCommander; + this.dynamicTask = dynamicTask; + } + + @Override + public void run() { + if (dynamicTask.get(taskKey) != null) { + dynamicTask.stop(taskKey); + } + SIPRequest sipRequest = null; + try { + sipRequest = sipCommander.mobilePositionSubscribe(device, request, eventResult -> { + // 成功 + logger.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失败"); + this.request = null; + } + },eventResult -> { + this.request = null; + // 失败 + logger.warn("[移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + dynamicTask.startDelay(taskKey, MobilePositionSubscribeTask.this, 2000); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 移动位置订阅: {}", e.getMessage()); + } + if (sipRequest != null) { + this.request = sipRequest; + } + + } + + @Override + public void stop() { + /** + * dialog 的各个状态 + * EARLY-> Early state状态-初始请求发送以后,收到了一个临时响应消息 + * CONFIRMED-> Confirmed Dialog状态-已确认 + * COMPLETED-> Completed Dialog状态-已完成 + * TERMINATED-> Terminated Dialog状态-终止 + */ + if (dynamicTask.get(taskKey) != null) { + dynamicTask.stop(taskKey); + } + device.setSubscribeCycleForMobilePosition(0); + try { + sipCommander.mobilePositionSubscribe(device, request, eventResult -> { + ResponseEvent event = (ResponseEvent) eventResult.event; + if (event.getResponse().getRawContent() != null) { + // 成功 + logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId()); + }else { + // 成功 + logger.info("[取消移动位置订阅]成功: {}", device.getDeviceId()); + } + },eventResult -> { + // 失败 + logger.warn("[取消移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.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 116236f5..52356c1d 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 @@ -1,5 +1,6 @@ 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.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.response.ISIPResponseProcessor; @@ -12,9 +13,10 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import javax.sip.*; -import javax.sip.header.CSeqHeader; -import javax.sip.header.CallIdHeader; +import javax.sip.header.*; +import javax.sip.message.Request; import javax.sip.message.Response; +import java.net.InetAddress; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -28,16 +30,15 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { private final static Logger logger = LoggerFactory.getLogger(SIPProcessorObserver.class); - private static Map requestProcessorMap = new ConcurrentHashMap<>(); + private static Map requestProcessorMap = new ConcurrentHashMap<>(); private static Map responseProcessorMap = new ConcurrentHashMap<>(); private static ITimeoutProcessor timeoutProcessor; @Autowired private SipSubscribe sipSubscribe; -// @Autowired -// @Qualifier(value = "taskExecutor") -// private ThreadPoolTaskExecutor poolTaskExecutor; + @Autowired + private EventPublisher eventPublisher; /** * 添加 request订阅 @@ -62,7 +63,7 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { * @param processor 处理程序 */ public void addTimeoutProcessor(ITimeoutProcessor processor) { - this.timeoutProcessor = processor; + timeoutProcessor = processor; } /** @@ -70,12 +71,13 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { * @param requestEvent RequestEvent事件 */ @Override - @Async + @Async("taskExecutor") public void processRequest(RequestEvent requestEvent) { String method = requestEvent.getRequest().getMethod(); ISIPRequestProcessor sipRequestProcessor = requestProcessorMap.get(method); if (sipRequestProcessor == null) { logger.warn("不支持方法{}的request", method); + // TODO 回复错误玛 return; } requestProcessorMap.get(method).process(requestEvent); @@ -87,34 +89,34 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { * @param responseEvent responseEvent事件 */ @Override - @Async + @Async("taskExecutor") public void processResponse(ResponseEvent responseEvent) { - logger.debug(responseEvent.getResponse().toString()); Response response = responseEvent.getResponse(); - logger.debug(responseEvent.getResponse().toString()); int status = response.getStatusCode(); - if (((status >= 200) && (status < 300)) || status == 401) { // Success! -// ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt); + + // 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 (responseEvent.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { + if (status != Response.UNAUTHORIZED && responseEvent.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) { CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME); 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); } } } - } else if ((status >= 100) && (status < 200)) { + } else if ((status >= Response.TRYING) && (status < Response.OK)) { // 增加其它无需回复的响应,如101、180等 } else { - logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/); + logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()); if (responseEvent.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) { CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME); if (callIdHeader != null) { @@ -122,6 +124,7 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { if (subscribe != null) { SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent); subscribe.response(eventResult); + sipSubscribe.removeErrorSubscribe(callIdHeader.getCallId()); } } } @@ -139,17 +142,58 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { */ @Override public void processTimeout(TimeoutEvent timeoutEvent) { - if(timeoutProcessor != null) { - timeoutProcessor.process(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); } @Override public void processIOException(IOExceptionEvent exceptionEvent) { + System.out.println("processIOException"); } @Override public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) { +// if (transactionTerminatedEvent.isServerTransaction()) { +// ServerTransaction serverTransaction = transactionTerminatedEvent.getServerTransaction(); +// serverTransaction.get +// } + + +// Transaction transaction = null; +// System.out.println("processTransactionTerminated"); +// if (transactionTerminatedEvent.isServerTransaction()) { +// transaction = transactionTerminatedEvent.getServerTransaction(); +// }else { +// transaction = transactionTerminatedEvent.getClientTransaction(); +// } +// +// System.out.println(transaction.getBranchId()); +// System.out.println(transaction.getState()); +// System.out.println(transaction.getRequest().getMethod()); +// CallIdHeader header = (CallIdHeader)transaction.getRequest().getHeader(CallIdHeader.NAME); +// SipSubscribe.EventResult terminatedEventEventResult = new SipSubscribe.EventResult<>(transactionTerminatedEvent); + +// sipSubscribe.getErrorSubscribe(header.getCallId()).response(terminatedEventEventResult); } @Override 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 new file mode 100644 index 00000000..0aff21d2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java @@ -0,0 +1,132 @@ +package com.genersoft.iot.vmp.gb28181.transmit; + +import com.genersoft.iot.vmp.gb28181.SipLayer; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; + +import javax.sip.SipException; +import javax.sip.header.CallIdHeader; +import javax.sip.header.UserAgentHeader; +import javax.sip.header.ViaHeader; +import javax.sip.message.Message; +import javax.sip.message.Request; +import javax.sip.message.Response; +import java.text.ParseException; + +/** + * 发送SIP消息 + * @author lin + */ +@Component +public class SIPSender { + + private final Logger logger = LoggerFactory.getLogger(SIPSender.class); + + @Autowired + private SipLayer sipLayer; + + @Autowired + private GitUtil gitUtil; + + @Autowired + private SipSubscribe sipSubscribe; + + public void transmitRequest(String ip, Message message) throws SipException, ParseException { + transmitRequest(ip, message, null, null); + } + + public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent) throws SipException, ParseException { + transmitRequest(ip, message, errorEvent, null); + } + + public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException { + 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(sipLayer.getSipFactory(), gitUtil)); + } catch (ParseException e) { + logger.error("添加UserAgentHeader失败", e); + } + } + + CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME); + // 添加错误订阅 + if (errorEvent != null) { + sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> { + errorEvent.response(eventResult); + sipSubscribe.removeErrorSubscribe(eventResult.callId); + sipSubscribe.removeOkSubscribe(eventResult.callId); + })); + } + // 添加订阅 + if (okEvent != null) { + sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> { + okEvent.response(eventResult); + sipSubscribe.removeOkSubscribe(eventResult.callId); + sipSubscribe.removeErrorSubscribe(eventResult.callId); + }); + } + 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); + } + } + } + + public CallIdHeader getNewCallIdHeader(String ip, String transport){ + if (ObjectUtils.isEmpty(transport)) { + return sipLayer.getUdpSipProvider().getNewCallId(); + } + SipProviderImpl sipProvider; + if (ObjectUtils.isEmpty(ip)) { + sipProvider = transport.equalsIgnoreCase("TCP") ? sipLayer.getTcpSipProvider() + : sipLayer.getUdpSipProvider(); + }else { + sipProvider = transport.equalsIgnoreCase("TCP") ? sipLayer.getTcpSipProvider(ip) + : sipLayer.getUdpSipProvider(ip); + } + + if (sipProvider == null) { + sipProvider = sipLayer.getUdpSipProvider(); + } + + if (sipProvider != null) { + return sipProvider.getNewCallId(); + }else { + logger.warn("[新建CallIdHeader失败], ip={}, transport={}", ip, transport); + return null; + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java deleted file mode 100644 index dff74cfe..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.callback; - -import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; -import com.genersoft.iot.vmp.gb28181.bean.RecordItem; -import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.RecordInfoResponseMessageHandler; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; -import org.slf4j.Logger; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.TimeUnit; - -@SuppressWarnings("unchecked") -public class CheckForAllRecordsThread extends Thread { - - private String key; - - private RecordInfo recordInfo; - - private RedisUtil redis; - - private Logger logger; - - private DeferredResultHolder deferredResultHolder; - - public CheckForAllRecordsThread(String key, RecordInfo recordInfo) { - this.key = key; - this.recordInfo = recordInfo; - } - - public void run() { - - String cacheKey = this.key; - - for (long stop = System.nanoTime() + TimeUnit.SECONDS.toNanos(10); stop > System.nanoTime();) { - List cacheKeys = redis.scan(cacheKey + "_*"); - List totalRecordList = new ArrayList(); - for (int i = 0; i < cacheKeys.size(); i++) { - totalRecordList.addAll((List) redis.get(cacheKeys.get(i).toString())); - } - if (totalRecordList.size() < this.recordInfo.getSumNum()) { - logger.info("已获取" + totalRecordList.size() + "项录像数据,共" + this.recordInfo.getSumNum() + "项"); - } else { - logger.info("录像数据已全部获取,共 {} 项", this.recordInfo.getSumNum()); - this.recordInfo.setRecordList(totalRecordList); - for (int i = 0; i < cacheKeys.size(); i++) { - redis.del(cacheKeys.get(i).toString()); - } - break; - } - } - // 自然顺序排序, 元素进行升序排列 - this.recordInfo.getRecordList().sort(Comparator.naturalOrder()); - RequestMessage msg = new RequestMessage(); - msg.setKey(DeferredResultHolder.CALLBACK_CMD_RECORDINFO + recordInfo.getDeviceId() + recordInfo.getSn()); - msg.setData(recordInfo); - deferredResultHolder.invokeAllResult(msg); - logger.info("处理完成,返回结果"); - RecordInfoResponseMessageHandler.threadNameList.remove(cacheKey); - } - - public void setRedis(RedisUtil redis) { - this.redis = redis; - } - - public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) { - this.deferredResultHolder = deferredResultHolder; - } - - public void setLogger(Logger logger) { - this.logger = logger; - } - -} 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 48c4939a..a351445e 100644 --- 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 @@ -1,15 +1,15 @@ package com.genersoft.iot.vmp.gb28181.transmit.callback; -import java.util.HashMap; +import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.web.context.request.async.DeferredResult; + +import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.async.DeferredResult; - /** * @description: 异步请求处理 * @author: swwheihei @@ -35,12 +35,14 @@ public class DeferredResultHolder { public static final String CALLBACK_CMD_PLAY = "CALLBACK_PLAY"; - public static final String CALLBACK_CMD_PLAYBACK = "CALLBACK_PLAY"; + public static final String CALLBACK_CMD_PLAYBACK = "CALLBACK_PLAYBACK"; public static final String CALLBACK_CMD_DOWNLOAD = "CALLBACK_DOWNLOAD"; public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP"; + public static final String UPLOAD_FILE_CHANNEL = "UPLOAD_FILE_CHANNEL"; + public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION"; public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY"; @@ -49,27 +51,48 @@ public class DeferredResultHolder { public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST"; - private Map> map = new ConcurrentHashMap<>(); + private Map> map = new ConcurrentHashMap<>(); - public void put(String key, String id, DeferredResult result) { - Map deferredResultMap = map.get(key); + public void put(String key, String id, DeferredResultEx result) { + Map deferredResultMap = map.get(key); if (deferredResultMap == null) { deferredResultMap = new ConcurrentHashMap<>(); map.put(key, deferredResultMap); } deferredResultMap.put(id, result); } + + 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)); + } - public DeferredResult get(String key, String id) { - Map deferredResultMap = map.get(key); - if (deferredResultMap == null) return null; + public DeferredResultEx get(String key, String id) { + Map deferredResultMap = map.get(key); + if (deferredResultMap == null || ObjectUtils.isEmpty(id)) { + return null; + } return deferredResultMap.get(id); } + public Collection getAllByKey(String key) { + Map deferredResultMap = map.get(key); + if (deferredResultMap == null) { + return null; + } + return deferredResultMap.values(); + } + public boolean exist(String key, String id){ - if (key == null) return false; - Map deferredResultMap = map.get(key); + if (key == null) { + return false; + } + Map deferredResultMap = map.get(key); if (id == null) { return deferredResultMap != null; }else { @@ -82,15 +105,15 @@ public class DeferredResultHolder { * @param msg */ public void invokeResult(RequestMessage msg) { - Map deferredResultMap = map.get(msg.getKey()); + Map deferredResultMap = map.get(msg.getKey()); if (deferredResultMap == null) { return; } - DeferredResult result = deferredResultMap.get(msg.getId()); + DeferredResultEx result = deferredResultMap.get(msg.getId()); if (result == null) { return; } - result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK)); + result.getDeferredResult().setResult(msg.getData()); deferredResultMap.remove(msg.getId()); if (deferredResultMap.size() == 0) { map.remove(msg.getKey()); @@ -102,19 +125,30 @@ public class DeferredResultHolder { * @param msg */ public void invokeAllResult(RequestMessage msg) { - Map deferredResultMap = map.get(msg.getKey()); + Map deferredResultMap = map.get(msg.getKey()); if (deferredResultMap == null) { return; } - Set ids = deferredResultMap.keySet(); - for (String id : ids) { - DeferredResult result = deferredResultMap.get(id); - if (result == null) { + synchronized (this) { + deferredResultMap = map.get(msg.getKey()); + if (deferredResultMap == null) { return; } - result.setResult(ResponseEntity.ok().body(msg.getData())); - } - map.remove(msg.getKey()); + Set ids = deferredResultMap.keySet(); + for (String id : ids) { + DeferredResultEx result = deferredResultMap.get(id); + if (result == null) { + return; + } + if (result.getFilter() != null) { + Object handler = result.getFilter().handler(msg.getData()); + result.getDeferredResult().setResult(handler); + }else { + result.getDeferredResult().setResult(msg.getData()); + } + } + map.remove(msg.getKey()); + } } } 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 6e96dac3..eebed4eb 100644 --- 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 @@ -1,11 +1,19 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamCallback; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import gov.nist.javax.sip.message.SIPRequest; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; /** * @description:设备能力接口,用于定义设备的控制、查询能力 @@ -21,9 +29,8 @@ public interface ISIPCommander { * @param channelId 预览通道 * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param moveSpeed 镜头移动速度 */ - boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown); + void ptzdirectCmd(Device device,String channelId,int leftRight, int upDown) throws InvalidArgumentException, ParseException, SipException; /** * 云台方向放控制 @@ -34,7 +41,7 @@ public interface ISIPCommander { * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 * @param moveSpeed 镜头移动速度 */ - boolean ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed); + void ptzdirectCmd(Device device,String channelId,int leftRight, int upDown, int moveSpeed) throws InvalidArgumentException, ParseException, SipException; /** * 云台缩放控制,使用配置文件中的默认镜头缩放速度 @@ -43,7 +50,7 @@ public interface ISIPCommander { * @param channelId 预览通道 * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 */ - boolean ptzZoomCmd(Device device,String channelId,int inOut); + void ptzZoomCmd(Device device,String channelId,int inOut) throws InvalidArgumentException, ParseException, SipException; /** * 云台缩放控制 @@ -51,9 +58,8 @@ public interface ISIPCommander { * @param device 控制设备 * @param channelId 预览通道 * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - * @param zoomSpeed 镜头缩放速度 */ - boolean ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed); + void ptzZoomCmd(Device device,String channelId,int inOut, int moveSpeed) throws InvalidArgumentException, ParseException, SipException; /** * 云台控制,支持方向与缩放控制 @@ -66,7 +72,7 @@ public interface ISIPCommander { * @param moveSpeed 镜头移动速度 * @param zoomSpeed 镜头缩放速度 */ - boolean ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed); + void ptzCmd(Device device,String channelId,int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) throws InvalidArgumentException, SipException, ParseException; /** * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 @@ -78,7 +84,7 @@ public interface ISIPCommander { * @param parameter2 数据2 * @param combineCode2 组合码2 */ - boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2); + void frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) throws SipException, InvalidArgumentException, ParseException; /** * 前端控制指令(用于转发上级指令) @@ -86,15 +92,15 @@ public interface ISIPCommander { * @param channelId 预览通道 * @param cmdString 前端控制指令串 */ - boolean fronEndCmd(Device device, String channelId, String cmdString); + void fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; /** * 请求预览视频流 * @param device 视频设备 * @param channelId 预览通道 */ - void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); - + void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + /** * 请求回放视频流 * @@ -103,7 +109,7 @@ public interface ISIPCommander { * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss */ - void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); + void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime,InviteStreamCallback inviteStreamCallback, InviteStreamCallback event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 请求历史媒体下载 @@ -114,49 +120,61 @@ public interface ISIPCommander { * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss * @param downloadSpeed 下载倍速参数 */ - void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); + void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent, + SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 视频流停止 */ - void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent); - void streamByeCmd(String deviceId, String channelId); + void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException; + + void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException; /** * 回放暂停 */ - void playPauseCmd(Device device, StreamInfo streamInfo); + void playPauseCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException; /** * 回放恢复 */ - void playResumeCmd(Device device, StreamInfo streamInfo); + void playResumeCmd(Device device, StreamInfo streamInfo) throws InvalidArgumentException, ParseException, SipException; /** * 回放拖动播放 */ - void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime); + void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) throws InvalidArgumentException, ParseException, SipException; /** * 回放倍速播放 */ - void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed); - + void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) throws InvalidArgumentException, ParseException, SipException; + /** + * 回放控制 + * @param device + * @param streamInfo + * @param content + */ + void playbackControlCmd(Device device, StreamInfo streamInfo, String content,SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException; + + + /** * 语音广播 * * @param device 视频设备 * @param channelId 预览通道 */ - boolean audioBroadcastCmd(Device device,String channelId); + void audioBroadcastCmd(Device device,String channelId); /** * 语音广播 * * @param device 视频设备 */ - void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent); - boolean audioBroadcastCmd(Device device); + void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException; + void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException; /** * 音视频录像控制 @@ -165,22 +183,21 @@ public interface ISIPCommander { * @param channelId 预览通道 * @param recordCmdStr 录像命令:Record / StopRecord */ - boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent); + void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 远程启动控制命令 * * @param device 视频设备 */ - boolean teleBootCmd(Device device); + void teleBootCmd(Device device) throws InvalidArgumentException, SipException, ParseException; /** * 报警布防/撤防命令 * * @param device 视频设备 - * @param setGuard true: SetGuard, false: ResetGuard */ - boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent); + void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 报警复位命令 @@ -189,7 +206,7 @@ public interface ISIPCommander { * @param alarmMethod 报警方式(可选) * @param alarmType 报警类型(可选) */ - boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent); + void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 @@ -197,7 +214,7 @@ public interface ISIPCommander { * @param device 视频设备 * @param channelId 预览通道 */ - boolean iFrameCmd(Device device, String channelId); + void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException; /** * 看守位控制命令 @@ -207,16 +224,16 @@ public interface ISIPCommander { * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 */ - boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent); + void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 设备配置命令 * * @param device 视频设备 */ - boolean deviceConfigCmd(Device device); + void deviceConfigCmd(Device device); - /** + /** * 设备配置命令:basicParam * * @param device 视频设备 @@ -226,14 +243,14 @@ public interface ISIPCommander { * @param heartBeatInterval 心跳间隔时间(可选) * @param heartBeatCount 心跳超时次数(可选) */ - boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent); - + void deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + /** * 查询设备状态 * * @param device 视频设备 */ - boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent); + void deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 查询设备信息 @@ -241,14 +258,14 @@ public interface ISIPCommander { * @param device 视频设备 * @return */ - boolean deviceInfoQuery(Device device); + void deviceInfoQuery(Device device) throws InvalidArgumentException, SipException, ParseException; /** * 查询目录列表 * * @param device 视频设备 */ - boolean catalogQuery(Device device, SipSubscribe.Event errorEvent); + void catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) throws SipException, InvalidArgumentException, ParseException; /** * 查询录像信息 @@ -258,7 +275,7 @@ public interface ISIPCommander { * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss * @param sn */ - boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, SipSubscribe.Event errorEvent); + void recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer Secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 查询报警信息 @@ -272,8 +289,8 @@ public interface ISIPCommander { * @param endTime 报警发生终止时间(可选) * @return true = 命令发送成功 */ - boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, - String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent); + void alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, + String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 查询设备配置 @@ -282,31 +299,29 @@ public interface ISIPCommander { * @param channelId 通道编码(可选) * @param configType 配置类型: */ - boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent); + void deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 查询设备预置位置 * * @param device 视频设备 */ - boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent); + void presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 查询移动设备位置数据 * * @param device 视频设备 */ - boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent); + void mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 订阅、取消订阅移动位置 * * @param device 视频设备 - * @param expires 订阅超时时间(值=0时为取消订阅) - * @param interval 上报时间间隔 * @return true = 命令发送成功 */ - boolean mobilePositionSubscribe(Device device, int expires, int interval); + SIPRequest mobilePositionSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent , SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; /** * 订阅、取消订阅报警信息 @@ -314,18 +329,36 @@ public interface ISIPCommander { * @param expires 订阅过期时间(0 = 取消订阅) * @param startPriority 报警起始级别(可选) * @param endPriority 报警终止级别(可选) - * @param alarmMethods 报警方式条件(可选) * @param alarmType 报警类型 * @param startTime 报警发生起始时间(可选) * @param endTime 报警发生终止时间(可选) * @return true = 命令发送成功 */ - boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime); + void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException; /** * 订阅、取消订阅目录信息 * @param device 视频设备 * @return true = 命令发送成功 */ - boolean catalogSubscribe(Device device, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent); + SIPRequest catalogSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; + + /** + * 拉框控制命令 + * + * @param device 控制设备 + * @param channelId 通道id + * @param cmdString 前端控制指令串 + */ + void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException; + + + /** + * 向设备发送报警NOTIFY消息, 用于互联结构下,此时将设备当成一个平级平台看待 + * @param device 设备 + * @param deviceAlarm 报警信息信息 + * @return + */ + void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException; + } 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 fe302938..0425356a 100644 --- 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 @@ -1,10 +1,14 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -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.service.bean.GPSMsgInfo; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; import javax.sip.header.WWWAuthenticateHeader; +import java.text.ParseException; +import java.util.List; public interface ISIPCommanderForPlatform { @@ -13,15 +17,15 @@ public interface ISIPCommanderForPlatform { * @param parentPlatform * @return */ - boolean register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent); - boolean register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent); + void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; + void register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException; /** * 向上级平台注销 * @param parentPlatform * @return */ - boolean unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent); + void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException; /** @@ -29,7 +33,7 @@ public interface ISIPCommanderForPlatform { * @param parentPlatform * @return callId(作为接受回复的判定) */ - String keepalive(ParentPlatform parentPlatform); + String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException; /** @@ -41,16 +45,17 @@ public interface ISIPCommanderForPlatform { * @param size * @return */ - boolean catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size); + void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException; + void catalogQuery(List channels, ParentPlatform parentPlatform, String sn, String fromTag) throws InvalidArgumentException, ParseException, SipException; /** * 向上级回复DeviceInfo查询信息 * @param parentPlatform 平台信息 - * @param sn - * @param fromTag + * @param sn SN + * @param fromTag FROM头的tag信息 * @return */ - boolean deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag); + void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException; /** * 向上级回复DeviceStatus查询信息 @@ -59,6 +64,61 @@ public interface ISIPCommanderForPlatform { * @param fromTag * @return */ - boolean deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag); + void deviceStatusResponse(ParentPlatform parentPlatform,String channelId, String sn, String fromTag,int status) throws SipException, InvalidArgumentException, ParseException; + /** + * 向上级回复移动位置订阅消息 + * @param parentPlatform 平台信息 + * @param gpsMsgInfo GPS信息 + * @param subscribeInfo 订阅相关的信息 + * @return + */ + void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException; + + /** + * 向上级回复报警消息 + * @param parentPlatform 平台信息 + * @param deviceAlarm 报警信息信息 + * @return + */ + void sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException; + + /** + * 回复catalog事件-增加/更新 + * @param parentPlatform + * @param deviceChannels + */ + void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException; + + /** + * 回复catalog事件-删除 + * @param parentPlatform + * @param deviceChannels + */ + void sendNotifyForCatalogOther(String type, ParentPlatform parentPlatform, List deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException; + + /** + * 回复recordInfo + * @param deviceChannel 通道信息 + * @param parentPlatform 平台信息 + * @param fromTag fromTag + * @param recordInfo 录像信息 + */ + void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException; + + /** + * 录像播放推送完成时发送MediaStatus消息 + * @param platform + * @param sendRtpItem + * @return + */ + void sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException; + + /** + * 向发起点播的上级回复bye + * @param platform 平台信息 + * @param callId callId + */ + void streamByeCmd(ParentPlatform platform, String callId) throws SipException, InvalidArgumentException, ParseException; + void streamByeCmd(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, 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 cc41af7c..0fe11c01 100644 --- 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 @@ -1,8 +1,15 @@ 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.SubscribeInfo; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.utils.GitUtil; import gov.nist.javax.sip.message.MessageFactoryImpl; +import gov.nist.javax.sip.message.SIPRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.DigestUtils; @@ -30,100 +37,68 @@ public class SIPRequestHeaderPlarformProvider { private SipConfig sipConfig; @Autowired - private SipFactory sipFactory; + private SipLayer sipLayer; + @Autowired + private GitUtil gitUtil; - public Request createKeetpaliveMessageRequest(ParentPlatform parentPlatform, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { + @Autowired + private IRedisCatchStorage redisCatchStorage; + + public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String viaTag, CallIdHeader callIdHeader, boolean isRegister) throws ParseException, InvalidArgumentException, PeerUnavailableException { Request request = null; - // sipuri - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); - // via - ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), - parentPlatform.getTransport(), viaTag); - viaHeader.setRPort(); - viaHeaders.add(viaHeader); - // from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), - sipConfig.getIp() + ":" + sipConfig.getPort()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); - // to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort() ); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); - - - // Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); - // ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE); - - request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, - toHeader, viaHeaders, maxForwards); - - List agentParam = new ArrayList<>(); - agentParam.add("wvp-pro"); - UserAgentHeader userAgentHeader = sipFactory.createHeaderFactory().createUserAgentHeader(agentParam); - request.addHeader(userAgentHeader); - - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); - request.setContent(content, contentTypeHeader); - return request; - } - - - public Request createRegisterRequest(@NotNull ParentPlatform platform, long CSeq, String fromTag, String viaTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { - Request request = null; - String sipAddress = sipConfig.getIp() + ":" + sipConfig.getPort(); + String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort(); //请求行 - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(platform.getServerGBId(), - platform.getServerIP() + ":" + platform.getServerPort()); + SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), + parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); //via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(platform.getServerIP(), platform.getServerPort(), platform.getTransport(), viaTag); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(), parentPlatform.getServerPort(), parentPlatform.getTransport(), viaTag); viaHeader.setRPort(); viaHeaders.add(viaHeader); //from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(platform.getDeviceGBId(),sipAddress); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(platform.getDeviceGBId(),sipAddress); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); - - + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null); //Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); //ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER); - request = sipFactory.createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader, + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER); + request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory() - .createSipURI(platform.getDeviceGBId(), sipAddress)); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory() + .createSipURI(parentPlatform.getDeviceGBId(), sipAddress)); + request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress)); - ExpiresHeader expires = sipFactory.createHeaderFactory().createExpiresHeader(Integer.parseInt(platform.getExpires())); + ExpiresHeader expires = sipLayer.getSipFactory().createHeaderFactory().createExpiresHeader(isRegister ? parentPlatform.getExpires() : 0); request.addHeader(expires); - List agentParam = new ArrayList<>(); - agentParam.add("wvp-pro"); - UserAgentHeader userAgentHeader = sipFactory.createHeaderFactory().createUserAgentHeader(agentParam); - request.addHeader(userAgentHeader); + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); return request; } public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String viaTag, - String callId, WWWAuthenticateHeader www , CallIdHeader callIdHeader) throws ParseException, PeerUnavailableException, InvalidArgumentException { + String callId, WWWAuthenticateHeader www , CallIdHeader callIdHeader, boolean isRegister) throws ParseException, PeerUnavailableException, InvalidArgumentException { - Request registerRequest = createRegisterRequest(parentPlatform, 2L, fromTag, viaTag, callIdHeader); - + Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, viaTag, callIdHeader, isRegister); + SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); + if (www == null) { + AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader("Digest"); + authorizationHeader.setUsername(parentPlatform.getDeviceGBId()); + authorizationHeader.setURI(requestURI); + authorizationHeader.setAlgorithm("MD5"); + registerRequest.addHeader(authorizationHeader); + return registerRequest; + } String realm = www.getRealm(); String nonce = www.getNonce(); String scheme = www.getScheme(); @@ -134,16 +109,15 @@ public class SIPRequestHeaderPlarformProvider { callIdHeader.setCallId(callId); - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort()); String cNonce = null; String nc = "00000001"; if (qop != null) { - if ("auth".equals(qop)) { + if ("auth".equalsIgnoreCase(qop)) { // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。 // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护 cNonce = UUID.randomUUID().toString(); - }else if ("auth-int".equals(qop)){ + }else if ("auth-int".equalsIgnoreCase(qop)){ // TODO } } @@ -167,7 +141,7 @@ public class SIPRequestHeaderPlarformProvider { String RESPONSE = DigestUtils.md5DigestAsHex(reStr.toString().getBytes()); - AuthorizationHeader authorizationHeader = sipFactory.createHeaderFactory().createAuthorizationHeader(scheme); + AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader(scheme); authorizationHeader.setUsername(parentPlatform.getDeviceGBId()); authorizationHeader.setRealm(realm); authorizationHeader.setNonce(nonce); @@ -184,43 +158,154 @@ public class SIPRequestHeaderPlarformProvider { return registerRequest; } + public Request createMessageRequest(ParentPlatform parentPlatform, String content, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException { + CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); + return createMessageRequest(parentPlatform, content, sendRtpItem.getToTag(), SipUtils.getNewViaTag(), sendRtpItem.getFromTag(), callIdHeader); + } - public Request createMessageRequest(ParentPlatform parentPlatform, String content, String fromTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException { + public Request createMessageRequest(ParentPlatform 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 { Request request = null; + String serverAddress = parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort(); // sipuri - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort()); + SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()), - parentPlatform.getTransport(), null); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()), + parentPlatform.getTransport(), viaTag); viaHeader.setRPort(); viaHeaders.add(viaHeader); // from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), - parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); + // SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), parentPlatform.getDeviceIp() + ":" + parentPlatform.getDeviceIp()); + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); // to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, null); + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, toTag); // Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); // ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE); - MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory(); + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE); + MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory(); // 设置编码, 防止中文乱码 - messageFactory.setDefaultContentEncodingCharset("gb2312"); + messageFactory.setDefaultContentEncodingCharset(parentPlatform.getCharacterSet()); request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); - List agentParam = new ArrayList<>(); - agentParam.add("wvp-pro"); - UserAgentHeader userAgentHeader = sipFactory.createHeaderFactory().createUserAgentHeader(agentParam); - request.addHeader(userAgentHeader); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + + ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); request.setContent(content, contentTypeHeader); return request; } + + public SIPRequest createNotifyRequest(ParentPlatform parentPlatform, String content, SubscribeInfo subscribeInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException { + SIPRequest request = null; + // sipuri + SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort()); + // via + ArrayList viaHeaders = new ArrayList<>(); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()), + parentPlatform.getTransport(), SipUtils.getNewViaTag()); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), + parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse().getToTag()); + // to + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain()); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest().getFromTag()); + + // Forwards + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.NOTIFY); + MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset("gb2312"); + + CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest().getCallIdHeader().getCallId()); + + request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + + EventHeader event = sipLayer.getSipFactory().createHeaderFactory().createEventHeader(subscribeInfo.getEventType()); + if (subscribeInfo.getEventId() != null) { + event.setEventId(subscribeInfo.getEventId()); + } + + request.addHeader(event); + + SubscriptionStateHeader active = sipLayer.getSipFactory().createHeaderFactory().createSubscriptionStateHeader("active"); + request.setHeader(active); + + String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort(); + Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory() + .createSipURI(parentPlatform.getDeviceGBId(), sipAddress)); + request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress)); + + ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); + request.setContent(content, contentTypeHeader); + return request; + } + + public SIPRequest createByeRequest(ParentPlatform platform, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException { + + if (sendRtpItem == null ) { + return null; + } + + SIPRequest request = null; + // sipuri + SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort()); + // via + ArrayList viaHeaders = new ArrayList<>(); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(platform.getDeviceIp(), Integer.parseInt(platform.getDevicePort()), + platform.getTransport(), SipUtils.getNewViaTag()); + viaHeader.setRPort(); + viaHeaders.add(viaHeader); + // from + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getDeviceGBId(), + platform.getDeviceIp() + ":" + platform.getDevicePort()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag()); + // to + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerGBDomain()); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, sendRtpItem.getFromTag()); + + // Forwards + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); + // ceq + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); + MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset("gb2312"); + + CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId()); + + request = (SIPRequest) messageFactory.createRequest(requestURI, Request.BYE, callIdHeader, cSeqHeader, fromHeader, + toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + + String sipAddress = platform.getDeviceIp() + ":" + platform.getDevicePort(); + Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory() + .createSipURI(platform.getDeviceGBId(), sipAddress)); + request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress)); + + return request; + } } 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 6306ab62..40d049cb 100644 --- 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 @@ -1,25 +1,27 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd; -import java.text.ParseException; -import java.util.ArrayList; +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; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; -import javax.sip.Dialog; import javax.sip.InvalidArgumentException; import javax.sip.PeerUnavailableException; -import javax.sip.SipFactory; +import javax.sip.SipException; import javax.sip.address.Address; import javax.sip.address.SipURI; import javax.sip.header.*; import javax.sip.message.Request; - -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.bean.Device; +import java.text.ParseException; +import java.util.ArrayList; /** * @description:摄像头命令request创造器 TODO 冗余代码太多待优化 @@ -33,7 +35,10 @@ public class SIPRequestHeaderProvider { private SipConfig sipConfig; @Autowired - private SipFactory sipFactory; + private SipLayer sipLayer; + + @Autowired + private GitUtil gitUtil; @Autowired private IRedisCatchStorage redisCatchStorage; @@ -44,30 +49,32 @@ public class SIPRequestHeaderProvider { public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { Request request = null; // sipuri - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag); viaHeader.setRPort(); viaHeaders.add(viaHeader); // from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), - sipConfig.getIp() + ":" + sipConfig.getPort()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); // to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, toTag); // Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); // ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE); + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE); - request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, + request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + + ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); request.setContent(content, contentTypeHeader); return request; } @@ -75,36 +82,39 @@ public class SIPRequestHeaderProvider { public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { Request request = null; //请求行 - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); + SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress()); //via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag); + HeaderFactory headerFactory = sipLayer.getSipFactory().createHeaderFactory(); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag); viaHeader.setRPort(); viaHeaders.add(viaHeader); //from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack //to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null); //Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); //ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); - request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); - - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort())); - // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE); + request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + + Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + // Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort())); + request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress)); // Subject - SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0)); + SubjectHeader subjectHeader = sipLayer.getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0)); request.addHeader(subjectHeader); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); + ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); request.setContent(content, contentTypeHeader); return request; } @@ -112,161 +122,196 @@ public class SIPRequestHeaderProvider { public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException { Request request = null; //请求行 - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); + SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag); viaHeader.setRPort(); viaHeaders.add(viaHeader); //from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack //to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, sipConfig.getDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null); + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress()); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null); //Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); //ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE); - request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE); + request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort())); - // Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + // Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort())); + request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + // Subject - SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0)); + SubjectHeader subjectHeader = sipLayer.getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0)); request.addHeader(subjectHeader); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); + ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); request.setContent(content, contentTypeHeader); return request; } - public Request createByteRequest(Device device, String channelId, String viaTag, String fromTag, String toTag, String callId) throws ParseException, InvalidArgumentException, PeerUnavailableException { + public Request createByteRequest(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException { Request request = null; //请求行 - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress()); + SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); viaHeaders.add(viaHeader); //from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag()); //to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,toTag); + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId,device.getHostAddress()); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag()); //Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); //ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.BYE); - CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(callId); - request = sipFactory.createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE); + CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId()); + request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort())); + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + + Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); return request; } - public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { + public Request createSubscribeRequest(Device device, String content, SIPRequest requestOld, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { Request request = null; // sipuri - SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), - device.getTransport(), viaTag); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), + device.getTransport(), SipUtils.getNewViaTag()); viaHeader.setRPort(); viaHeaders.add(viaHeader); // from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), - sipConfig.getIp() + ":" + sipConfig.getPort()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag()); // to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag); + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag()); // Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); // ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE); + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.SUBSCRIBE); - request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, + request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress)); // Expires - ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires); + ExpiresHeader expireHeader = sipLayer.getSipFactory().createHeaderFactory().createExpiresHeader(expires); request.addHeader(expireHeader); // Event - EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event); + EventHeader eventHeader = sipLayer.getSipFactory().createHeaderFactory().createEventHeader(event); + + int random = (int) Math.floor(Math.random() * 10000); + eventHeader.setEventId(random + ""); request.addHeader(eventHeader); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml"); + ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml"); request.setContent(content, contentTypeHeader); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + return request; } - public Request createInfoRequest(Device device, StreamInfo streamInfo, String content, Long cseq) - throws PeerUnavailableException, ParseException, InvalidArgumentException { - Request request = null; - Dialog dialog = streamSession.getDialog(streamInfo.getDeviceID(), streamInfo.getChannelId()); - - SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), - device.getHostAddress()); + public SIPRequest createInfoRequest(Device device, String channelId, String content, SipTransactionInfo transactionInfo) + throws SipException, ParseException, InvalidArgumentException { + if (device == null || transactionInfo == null) { + return null; + } + SIPRequest request = null; + //请求行 + SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress()); // via ArrayList viaHeaders = new ArrayList(); - ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.getPort(), - device.getTransport(), null); - viaHeader.setRPort(); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag()); viaHeaders.add(viaHeader); - // from - SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), - sipConfig.getDomain()); - Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI); - FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, dialog.getLocalTag()); - // to - SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(streamInfo.getChannelId(), - sipConfig.getDomain()); - Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI); - ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, dialog.getRemoteTag()); + //from + SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain()); + Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI); + FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag()); + //to + SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId,device.getHostAddress()); + Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI); + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, transactionInfo.getToTag()); - // callid - CallIdHeader callIdHeader = dialog.getCallId(); + //Forwards + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); - // Forwards - MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70); - if (cseq == null) { - cseq = redisCatchStorage.getCSEQ(Request.INFO); + //ceq + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO); + CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId()); + request = (SIPRequest)sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + + Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort())); + request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + + if (content != null) { + ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", + "MANSRTSP"); + request.setContent(content, contentTypeHeader); } - // ceq - CSeqHeader cSeqHeader = sipFactory.createHeaderFactory() - .createCSeqHeader(cseq, Request.INFO); + return request; + } - request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader, - fromHeader, toHeader, viaHeaders, maxForwards); - Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory() - .createSipURI(sipConfig.getId(), sipConfig.getIp() + ":" + sipConfig.getPort())); - request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); + public Request createAckRequest(String localIp, SipURI sipURI, SIPResponse sipResponse) throws ParseException, InvalidArgumentException, PeerUnavailableException { + + + // via + ArrayList viaHeaders = new ArrayList(); + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(localIp, sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag()); + viaHeaders.add(viaHeader); + + //Forwards + MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70); + + //ceq + CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK); + + Request request = sipLayer.getSipFactory().createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); + + Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), localIp + ":"+sipConfig.getPort())); + request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress)); + + request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil)); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", - "MANSRTSP"); - request.setContent(content, contentTypeHeader); return request; } } 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 dc0996a7..24155384 100644 --- 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 @@ -1,1658 +1,1404 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; +import com.genersoft.iot.vmp.conf.UserSetting; +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.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.DateUtil; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.bean.SSRCInfo; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import gov.nist.javax.sip.SipProviderImpl; -import gov.nist.javax.sip.SipStackImpl; +import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.message.SIPRequest; -import gov.nist.javax.sip.stack.SIPDialog; +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.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; -import javax.sip.*; -import javax.sip.address.SipURI; +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; import javax.sip.header.CallIdHeader; -import javax.sip.header.ViaHeader; import javax.sip.message.Request; -import java.lang.reflect.Field; import java.text.ParseException; -import java.util.HashSet; -/** - * @description:设备能力接口,用于定义设备的控制、查询能力 +/** + * @description:设备能力接口,用于定义设备的控制、查询能力 * @author: swwheihei - * @date: 2020年5月3日 下午9:22:48 + * @date: 2020年5月3日 下午9:22:48 */ @Component @DependsOn("sipLayer") public class SIPCommander implements ISIPCommander { - private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); - - @Autowired - private SipConfig sipConfig; + private final Logger logger = LoggerFactory.getLogger(SIPCommander.class); - @Autowired - @Qualifier(value="tcpSipProvider") - private SipProviderImpl tcpSipProvider; + @Autowired + private SipConfig sipConfig; - @Autowired - @Qualifier(value="udpSipProvider") - private SipProviderImpl udpSipProvider; + @Autowired + private SipLayer sipLayer; - @Autowired - private SIPRequestHeaderProvider headerProvider; - - @Autowired - private VideoStreamSessionManager streamSession; + @Autowired + private SIPSender sipSender; + + @Autowired + private SIPRequestHeaderProvider headerProvider; - @Autowired - private IVideoManagerStorager storager; + @Autowired + private VideoStreamSessionManager streamSession; - @Autowired - private IRedisCatchStorage redisCatchStorage; + @Autowired + private UserSetting userSetting; - @Autowired - private UserSetup userSetup; - - @Autowired - private ZLMHttpHookSubscribe subscribe; - - @Autowired - private SipSubscribe sipSubscribe; - - @Autowired - private IMediaServerService mediaServerService; + @Autowired + private ZlmHttpHookSubscribe subscribe; - /** - * 云台方向放控制,使用配置文件中的默认镜头移动速度 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - */ - @Override - public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) { - return ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getPtzSpeed(), 0); - } - /** - * 云台方向放控制 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param moveSpeed 镜头移动速度 - */ - @Override - public boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) { - return ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0); - } + @Autowired + private IMediaServerService mediaServerService; - /** - * 云台缩放控制,使用配置文件中的默认镜头缩放速度 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - */ - @Override - public boolean ptzZoomCmd(Device device, String channelId, int inOut) { - return ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getPtzSpeed()); - } - /** - * 云台缩放控制 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - * @param zoomSpeed 镜头缩放速度 - */ - @Override - public boolean ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) { - return ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed); - } - - /** - * 云台指令码计算 - * - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - * @param moveSpeed 镜头移动速度 默认 0XFF (0-255) - * @param zoomSpeed 镜头缩放速度 默认 0X1 (0-255) - */ - public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) { - int cmdCode = 0; - if (leftRight == 2) { - cmdCode|=0x01; // 右移 - } else if(leftRight == 1) { - cmdCode|=0x02; // 左移 - } - if (upDown == 2) { - cmdCode|=0x04; // 下移 - } else if(upDown == 1) { - cmdCode|=0x08; // 上移 - } - if (inOut == 2) { - cmdCode |= 0x10; // 放大 - } else if(inOut == 1) { - cmdCode |= 0x20; // 缩小 - } - StringBuilder builder = new StringBuilder("A50F01"); - String strTmp; - strTmp = String.format("%02X", cmdCode); - builder.append(strTmp, 0, 2); - strTmp = String.format("%02X", moveSpeed); - builder.append(strTmp, 0, 2); - builder.append(strTmp, 0, 2); - strTmp = String.format("%X", zoomSpeed); - builder.append(strTmp, 0, 1).append("0"); - //计算校验码 - int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100; - strTmp = String.format("%02X", checkCode); - builder.append(strTmp, 0, 2); - return builder.toString(); -} + /** + * 云台方向放控制,使用配置文件中的默认镜头移动速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + */ + @Override + public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown) throws InvalidArgumentException, ParseException, SipException { + ptzCmd(device, channelId, leftRight, upDown, 0, sipConfig.getPtzSpeed(), 0); + } - /** - * 云台指令码计算 - * - * @param cmdCode 指令码 - * @param parameter1 数据1 - * @param parameter2 数据2 - * @param combineCode2 组合码2 - */ + /** + * 云台方向放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param moveSpeed 镜头移动速度 + */ + @Override + public void ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed) throws InvalidArgumentException, ParseException, SipException { + ptzCmd(device, channelId, leftRight, upDown, 0, moveSpeed, 0); + } + + /** + * 云台缩放控制,使用配置文件中的默认镜头缩放速度 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + */ + @Override + public void ptzZoomCmd(Device device, String channelId, int inOut) throws InvalidArgumentException, ParseException, SipException { + ptzCmd(device, channelId, 0, 0, inOut, 0, sipConfig.getPtzSpeed()); + } + + /** + * 云台缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param zoomSpeed 镜头缩放速度 + */ + @Override + public void ptzZoomCmd(Device device, String channelId, int inOut, int zoomSpeed) throws InvalidArgumentException, ParseException, SipException { + ptzCmd(device, channelId, 0, 0, inOut, 0, zoomSpeed); + } + + /** + * 云台指令码计算 + * + * @param cmdCode 指令码 + * @param parameter1 数据1 + * @param parameter2 数据2 + * @param combineCode2 组合码2 + */ public static String frontEndCmdString(int cmdCode, int parameter1, int parameter2, int combineCode2) { - StringBuilder builder = new StringBuilder("A50F01"); - String strTmp; - strTmp = String.format("%02X", cmdCode); - builder.append(strTmp, 0, 2); - strTmp = String.format("%02X", parameter1); - builder.append(strTmp, 0, 2); - strTmp = String.format("%02X", parameter2); - builder.append(strTmp, 0, 2); - strTmp = String.format("%X", combineCode2); - builder.append(strTmp, 0, 1).append("0"); - //计算校验码 - int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100; - strTmp = String.format("%02X", checkCode); - builder.append(strTmp, 0, 2); - return builder.toString(); - } - - /** - * 云台控制,支持方向与缩放控制 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 - * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 - * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 - * @param moveSpeed 镜头移动速度 - * @param zoomSpeed 镜头缩放速度 - */ - @Override - public boolean ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, - int zoomSpeed) { - try { - String cmdStr= cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed); - StringBuffer ptzXml = new StringBuffer(200); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("DeviceControl\r\n"); - ptzXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - ptzXml.append("" + channelId + "\r\n"); - ptzXml.append("" + cmdStr + "\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader); - - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - return false; - } - - /** - * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 - * - * @param device 控制设备 - * @param channelId 预览通道 - * @param cmdCode 指令码 - * @param parameter1 数据1 - * @param parameter2 数据2 - * @param combineCode2 组合码2 - */ - @Override - public boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) { - try { - String cmdStr= frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2); - logger.info("控制字符串:" + cmdStr); - StringBuffer ptzXml = new StringBuffer(200); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("DeviceControl\r\n"); - ptzXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - ptzXml.append("" + channelId + "\r\n"); - ptzXml.append("" + cmdStr + "\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader); - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - return false; - } - - /** - * 前端控制指令(用于转发上级指令) - * @param device 控制设备 - * @param channelId 预览通道 - * @param cmdString 前端控制指令串 - */ - @Override - public boolean fronEndCmd(Device device, String channelId, String cmdString) { - try { - StringBuffer ptzXml = new StringBuffer(200); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("DeviceControl\r\n"); - ptzXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - ptzXml.append("" + channelId + "\r\n"); - ptzXml.append("" + cmdString + "\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - ptzXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), "z9hG4bK-ViaPtz-" + tm, "FromPtz" + tm, null, callIdHeader); - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - return false; - } - - /** - * 请求预览视频流 - * @param device 视频设备 - * @param channelId 预览通道 - * @param event hook订阅 - * @param errorEvent sip错误订阅 - */ - @Override - public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent) { - String streamId = ssrcInfo.getStreamId(); - try { - if (device == null) return; - String streamMode = device.getStreamMode().toUpperCase(); - - logger.info("{} 分配的ZLM为: {} [{}:{}]", streamId, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); - // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("stream", streamId); - subscribeKey.put("regist", true); - subscribeKey.put("mediaServerId", mediaServerItem.getId()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ - if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return; - event.response(mediaServerItemInUse, json); - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey); - }); - // - StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); - content.append("o="+ sipConfig.getId()+" 0 0 IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n"); - content.append("s=Play\r\n"); - content.append("c=IN IP4 "+ mediaServerItem.getSdpIp() +"\r\n"); - content.append("t=0 0\r\n"); - - if (userSetup.isSeniorSdp()) { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:126 H264/90000\r\n"); - content.append("a=rtpmap:125 H264S/90000\r\n"); - content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); - content.append("a=fmtp:99 profile-level-id=3\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - }else { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - } - - content.append("y="+ssrcInfo.getSsrc()+"\r\n");//ssrc - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), null, "FromInvt" + tm, null, ssrcInfo.getSsrc(), callIdHeader); - - String finalStreamId = streamId; - transmitRequest(device, request, (e -> { - streamSession.remove(device.getDeviceId(), channelId); - mediaServerService.releaseSsrc(mediaServerItem, ssrcInfo.getSsrc()); - errorEvent.response(e); - }), e ->{ - streamSession.put(device.getDeviceId(), channelId ,ssrcInfo.getSsrc(), finalStreamId, mediaServerItem.getId(), ((ResponseEvent)e.event).getClientTransaction()); - streamSession.put(device.getDeviceId(), channelId , e.dialog); - }); - - - } catch ( SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - /** - * 请求回放视频流 - * - * @param device 视频设备 - * @param channelId 预览通道 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss - */ - @Override - public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event - , SipSubscribe.Event errorEvent) { - try { - - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); - - // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("stream", ssrcInfo.getStreamId()); - subscribeKey.put("regist", true); - subscribeKey.put("mediaServerId", mediaServerItem.getId()); - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ - if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return; - event.response(mediaServerItemInUse, json); - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey); - }); - - StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); - content.append("o="+sipConfig.getId()+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); - content.append("s=Playback\r\n"); - content.append("u="+channelId+":0\r\n"); - content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); - content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" " - +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n"); - - - - String streamMode = device.getStreamMode().toUpperCase(); - - if (userSetup.isSeniorSdp()) { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:126 H264/90000\r\n"); - content.append("a=rtpmap:125 H264S/90000\r\n"); - content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); - content.append("a=fmtp:99 profile-level-id=3\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - }else { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - } - - content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc()); - - transmitRequest(device, request, errorEvent, okEvent -> { - ResponseEvent responseEvent = (ResponseEvent) okEvent.event; - streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), responseEvent.getClientTransaction()); - streamSession.put(device.getDeviceId(), channelId, okEvent.dialog); - }); - } catch ( SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - /** - * 请求历史媒体下载 - * - * @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(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event - , SipSubscribe.Event errorEvent) { - try { - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStreamId(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); - - // 添加订阅 - JSONObject subscribeKey = new JSONObject(); - subscribeKey.put("app", "rtp"); - subscribeKey.put("stream", ssrcInfo.getStreamId()); - subscribeKey.put("regist", true); - subscribeKey.put("mediaServerId", mediaServerItem.getId()); - logger.debug("录像回放添加订阅,订阅内容:" + subscribeKey.toString()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey, - (MediaServerItem mediaServerItemInUse, JSONObject json)->{ - if (userSetup.isWaitTrack() && json.getJSONArray("tracks") == null) return; - event.response(mediaServerItemInUse, json); - subscribe.removeSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, subscribeKey); - }); - - StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); - content.append("o="+sipConfig.getId()+" 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); - content.append("s=Download\r\n"); - content.append("u="+channelId+":0\r\n"); - content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); - content.append("t="+DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime)+" " - +DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) +"\r\n"); - - - - String streamMode = device.getStreamMode().toUpperCase(); - - if (userSetup.isSeniorSdp()) { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 126 125 99 34 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:126 H264/90000\r\n"); - content.append("a=rtpmap:125 H264S/90000\r\n"); - content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); - content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); - content.append("a=fmtp:99 profile-level-id=3\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - }else { - if("TCP-PASSIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" TCP/RTP/AVP 96 98 97\r\n"); - }else if("UDP".equals(streamMode)) { - content.append("m=video "+ ssrcInfo.getPort() +" RTP/AVP 96 98 97\r\n"); - } - content.append("a=recvonly\r\n"); - content.append("a=rtpmap:96 PS/90000\r\n"); - content.append("a=rtpmap:98 H264/90000\r\n"); - content.append("a=rtpmap:97 MPEG4/90000\r\n"); - if("TCP-PASSIVE".equals(streamMode)){ // tcp被动模式 - content.append("a=setup:passive\r\n"); - content.append("a=connection:new\r\n"); - }else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 - content.append("a=setup:active\r\n"); - content.append("a=connection:new\r\n"); - } - } - content.append("a=downloadspeed:" + downloadSpeed + "\r\n"); - - content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, "fromplybck" + tm, null, callIdHeader, ssrcInfo.getSsrc()); - - ClientTransaction transaction = transmitRequest(device, request, errorEvent); - streamSession.put(device.getDeviceId(), channelId, ssrcInfo.getSsrc(), ssrcInfo.getStreamId(), mediaServerItem.getId(), transaction); - - } catch ( SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - /** - * 视频流停止, 不使用回调 - */ - @Override - public void streamByeCmd(String deviceId, String channelId) { - streamByeCmd(deviceId, channelId, null); - } - - /** - * 视频流停止 - */ - @Override - public void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent) { - try { - ClientTransaction transaction = streamSession.getTransaction(deviceId, channelId); - if (transaction == null) { - logger.warn("[ {} -> {}]停止视频流的时候发现事务已丢失", deviceId, channelId); - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult<>(); - if (okEvent != null) { - okEvent.response(eventResult); - } - return; - } - SIPDialog dialog = streamSession.getDialog(deviceId, channelId); - if (dialog == null) { - logger.warn("[ {} -> {}]停止视频流的时候发现对话已丢失", deviceId, channelId); - return; - } - SipStack sipStack = udpSipProvider.getSipStack(); - SIPDialog sipDialog = ((SipStackImpl) sipStack).putDialog(dialog); - if (dialog != sipDialog) { - dialog = sipDialog; - }else { - dialog.setSipProvider(udpSipProvider); - try { - Field sipStackField = SIPDialog.class.getDeclaredField("sipStack"); - sipStackField.setAccessible(true); - sipStackField.set(dialog, sipStack); - Field eventListenersField = SIPDialog.class.getDeclaredField("eventListeners"); - eventListenersField.setAccessible(true); - eventListenersField.set(dialog, new HashSet<>()); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - } - } - - Request byeRequest = dialog.createRequest(Request.BYE); - SipURI byeURI = (SipURI) byeRequest.getRequestURI(); - SIPRequest request = (SIPRequest)transaction.getRequest(); - byeURI.setHost(request.getRemoteAddress().getHostName()); - byeURI.setPort(request.getRemotePort()); - ViaHeader viaHeader = (ViaHeader) byeRequest.getHeader(ViaHeader.NAME); - String protocol = viaHeader.getTransport().toUpperCase(); - ClientTransaction clientTransaction = null; - if("TCP".equals(protocol)) { - clientTransaction = tcpSipProvider.getNewClientTransaction(byeRequest); - } else if("UDP".equals(protocol)) { - clientTransaction = udpSipProvider.getNewClientTransaction(byeRequest); - } - - CallIdHeader callIdHeader = (CallIdHeader) byeRequest.getHeader(CallIdHeader.NAME); - if (okEvent != null) { - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); - } - - dialog.sendRequest(clientTransaction); - - SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(deviceId, channelId); - if (ssrcTransaction != null) { - MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransaction.getMediaServerId()); - mediaServerService.releaseSsrc(mediaServerItem, ssrcTransaction.getSsrc()); - streamSession.remove(deviceId, channelId); - } - } catch (SipException | ParseException e) { - e.printStackTrace(); - } - } - - /** - * 语音广播 - * - * @param device 视频设备 - * @param channelId 预览通道 - */ - @Override - public boolean audioBroadcastCmd(Device device, String channelId) { - // 改为新的实现 - return false; - } - - /** - * 语音广播 - * - * @param device 视频设备 - */ - @Override - public boolean audioBroadcastCmd(Device device) { - try { - StringBuffer broadcastXml = new StringBuffer(200); - broadcastXml.append("\r\n"); - broadcastXml.append("\r\n"); - broadcastXml.append("Broadcast\r\n"); - broadcastXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - broadcastXml.append("" + sipConfig.getId() + "\r\n"); - broadcastXml.append("" + device.getDeviceId() + "\r\n"); - broadcastXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader); - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - return false; - } - @Override - public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) { - try { - StringBuffer broadcastXml = new StringBuffer(200); - broadcastXml.append("\r\n"); - broadcastXml.append("\r\n"); - broadcastXml.append("Broadcast\r\n"); - broadcastXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - broadcastXml.append("" + sipConfig.getId() + "\r\n"); - broadcastXml.append("" + device.getDeviceId() + "\r\n"); - broadcastXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), "z9hG4bK-ViaBcst-" + tm, "FromBcst" + tm, null, callIdHeader); - transmitRequest(device, request, errorEvent); - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - - /** - * 音视频录像控制 - * - * @param device 视频设备 - * @param channelId 预览通道 - * @param recordCmdStr 录像命令:Record / StopRecord - */ - @Override - public boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (StringUtils.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("" + recordCmdStr + "\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromRecord" + tm, null, callIdHeader); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 远程启动控制命令 - * - * @param device 视频设备 - */ - @Override - public boolean teleBootCmd(Device device) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - cmdXml.append("Boot\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null, callIdHeader); - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 报警布防/撤防命令 - * - * @param device 视频设备 - * @param guardCmdStr "SetGuard"/"ResetGuard" - */ - @Override - public boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - cmdXml.append("" + guardCmdStr + "\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null, callIdHeader); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 报警复位命令 - * - * @param device 视频设备 - */ - @Override - public boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - cmdXml.append("ResetAlarm\r\n"); - if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) { - cmdXml.append("\r\n"); - } - if (!StringUtils.isEmpty(alarmMethod)) { - cmdXml.append("" + alarmMethod + "\r\n"); - } - if (!StringUtils.isEmpty(alarmType)) { - cmdXml.append("" + alarmType + "\r\n"); - } - if (!StringUtils.isEmpty(alarmMethod) || !StringUtils.isEmpty(alarmType)) { - cmdXml.append("\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null, callIdHeader); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 - * - * @param device 视频设备 - * @param channelId 预览通道 - */ - @Override - public boolean iFrameCmd(Device device, String channelId) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (StringUtils.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("Send\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromBoot" + tm, null, callIdHeader); - transmitRequest(device, request); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 看守位控制命令 - * - * @param device 视频设备 - * @param enabled 看守位使能:1 = 开启,0 = 关闭 - * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) - * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 - */ - @Override - public boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceControl\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (StringUtils.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("\r\n"); - if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) { - cmdXml.append("1\r\n"); - if (NumericUtil.isInteger(resetTime)) { - cmdXml.append("" + resetTime + "\r\n"); - } else { - cmdXml.append("0\r\n"); - } - if (NumericUtil.isInteger(presetIndex)) { - cmdXml.append("" + presetIndex + "\r\n"); - } else { - cmdXml.append("0\r\n"); - } - } else { - cmdXml.append("0\r\n"); - } - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromGuard" + tm, null, callIdHeader); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 设备配置命令 - * - * @param device 视频设备 - */ - @Override - public boolean deviceConfigCmd(Device device) { - // TODO Auto-generated method stub - return false; - } - - /** - * 设备配置命令:basicParam - * - * @param device 视频设备 - * @param channelId 通道编码(可选) - * @param name 设备/通道名称(可选) - * @param expiration 注册过期时间(可选) - * @param heartBeatInterval 心跳间隔时间(可选) - * @param heartBeatCount 心跳超时次数(可选) - */ - @Override - public boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, - String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("DeviceConfig\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (StringUtils.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("\r\n"); - if (!StringUtils.isEmpty(name)) { - cmdXml.append("" + name + "\r\n"); - } - if (NumericUtil.isInteger(expiration)) { - if (Integer.valueOf(expiration) > 0) { - cmdXml.append("" + expiration + "\r\n"); - } - } - if (NumericUtil.isInteger(heartBeatInterval)) { - if (Integer.valueOf(heartBeatInterval) > 0) { - cmdXml.append("" + heartBeatInterval + "\r\n"); - } - } - if (NumericUtil.isInteger(heartBeatCount)) { - if (Integer.valueOf(heartBeatCount) > 0) { - cmdXml.append("" + heartBeatCount + "\r\n"); - } - } - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null, callIdHeader); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询设备状态 - * - * @param device 视频设备 - */ - @Override - public boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) { - try { - StringBuffer catalogXml = new StringBuffer(200); - catalogXml.append("\r\n"); - catalogXml.append("\r\n"); - catalogXml.append("DeviceStatus\r\n"); - catalogXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - catalogXml.append("" + device.getDeviceId() + "\r\n"); - catalogXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, "FromStatus" + tm, null, callIdHeader); - - transmitRequest(device, request, errorEvent); - return true; - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询设备信息 - * - * @param device 视频设备 - */ - @Override - public boolean deviceInfoQuery(Device device) { - try { - StringBuffer catalogXml = new StringBuffer(200); - catalogXml.append("\r\n"); - catalogXml.append("\r\n"); - catalogXml.append("DeviceInfo\r\n"); - catalogXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - catalogXml.append("" + device.getDeviceId() + "\r\n"); - catalogXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaDeviceInfo-" + tm, "FromDev" + tm, null, callIdHeader); - - transmitRequest(device, request); - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - * 查询目录列表 - * - * @param device 视频设备 - */ - @Override - public boolean catalogQuery(Device device, SipSubscribe.Event errorEvent) { - // 清空通道 -// storager.cleanChannelsForDevice(device.getDeviceId()); - try { - StringBuffer catalogXml = new StringBuffer(200); - catalogXml.append("\r\n"); - catalogXml.append("\r\n"); - catalogXml.append("Catalog\r\n"); - catalogXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - catalogXml.append("" + device.getDeviceId() + "\r\n"); - catalogXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "z9hG4bK-ViaCatalog-" + tm, "FromCat" + tm, null, callIdHeader); - - transmitRequest(device, request, errorEvent); - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - * 查询录像信息 - * - * @param device 视频设备 - * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss - * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss - */ - @Override - public boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, SipSubscribe.Event errorEvent) { - - - try { - StringBuffer recordInfoXml = new StringBuffer(200); - recordInfoXml.append("\r\n"); - recordInfoXml.append("\r\n"); - recordInfoXml.append("RecordInfo\r\n"); - recordInfoXml.append("" + sn + "\r\n"); - recordInfoXml.append("" + channelId + "\r\n"); - recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "\r\n"); - recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "\r\n"); - recordInfoXml.append("0\r\n"); - // 大华NVR要求必须增加一个值为all的文本元素节点Type - recordInfoXml.append("all\r\n"); - recordInfoXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), - "z9hG4bK-ViaRecordInfo-" + tm, "fromRec" + tm, null, callIdHeader); - - transmitRequest(device, request, errorEvent); - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - * 查询报警信息 - * - * @param device 视频设备 - * @param startPriority 报警起始级别(可选) - * @param endPriority 报警终止级别(可选) - * @param alarmMethod 报警方式条件(可选) - * @param alarmType 报警类型 - * @param startTime 报警发生起始时间(可选) - * @param endTime 报警发生终止时间(可选) - * @return true = 命令发送成功 - */ - @Override - public boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType, - String startTime, String endTime, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("Alarm\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - if (!StringUtils.isEmpty(startPriority)) { - cmdXml.append("" + startPriority + "\r\n"); - } - if (!StringUtils.isEmpty(endPriority)) { - cmdXml.append("" + endPriority + "\r\n"); - } - if (!StringUtils.isEmpty(alarmMethod)) { - cmdXml.append("" + alarmMethod + "\r\n"); - } - if (!StringUtils.isEmpty(alarmType)) { - cmdXml.append("" + alarmType + "\r\n"); - } - if (!StringUtils.isEmpty(startTime)) { - cmdXml.append("" + startTime + "\r\n"); - } - if (!StringUtils.isEmpty(endTime)) { - cmdXml.append("" + endTime + "\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromAlarm" + tm, null, callIdHeader); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询设备配置 - * - * @param device 视频设备 - * @param channelId 通道编码(可选) - * @param configType 配置类型: - */ - @Override - public boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("ConfigDownload\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (StringUtils.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("" + configType + "\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null, callIdHeader); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询设备预置位置 - * - * @param device 视频设备 - */ - @Override - public boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("PresetQuery\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - if (StringUtils.isEmpty(channelId)) { - cmdXml.append("" + device.getDeviceId() + "\r\n"); - } else { - cmdXml.append("" + channelId + "\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, "FromConfig" + tm, null, callIdHeader); - transmitRequest(device, request, errorEvent); - return true; - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 查询移动设备位置数据 - * - * @param device 视频设备 - */ - @Override - public boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) { - try { - StringBuffer mobilePostitionXml = new StringBuffer(200); - mobilePostitionXml.append("\r\n"); - mobilePostitionXml.append("\r\n"); - mobilePostitionXml.append("MobilePosition\r\n"); - mobilePostitionXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - mobilePostitionXml.append("" + device.getDeviceId() + "\r\n"); - mobilePostitionXml.append("60\r\n"); - mobilePostitionXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, callIdHeader); - - transmitRequest(device, request, errorEvent); - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; - } - - /** - * 订阅、取消订阅移动位置 - * - * @param device 视频设备 - * @param expires 订阅超时时间 - * @param interval 上报时间间隔 - * @return true = 命令发送成功 - */ - public boolean mobilePositionSubscribe(Device device, int expires, int interval) { - try { - StringBuffer subscribePostitionXml = new StringBuffer(200); - subscribePostitionXml.append("\r\n"); - subscribePostitionXml.append("\r\n"); - subscribePostitionXml.append("MobilePosition\r\n"); - subscribePostitionXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - subscribePostitionXml.append("" + device.getDeviceId() + "\r\n"); - if (expires > 0) { - subscribePostitionXml.append("" + String.valueOf(interval) + "\r\n"); - } - subscribePostitionXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, expires, "presence" ,callIdHeader); //Position;id=" + tm.substring(tm.length() - 4)); - transmitRequest(device, request); - - return true; - - } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 订阅、取消订阅报警信息 - * - * @param device 视频设备 - * @param expires 订阅过期时间(0 = 取消订阅) - * @param startPriority 报警起始级别(可选) - * @param endPriority 报警终止级别(可选) - * @param alarmMethod 报警方式条件(可选) - * @param alarmType 报警类型 - * @param startTime 报警发生起始时间(可选) - * @param endTime 报警发生终止时间(可选) - * @return true = 命令发送成功 - */ - public boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("Alarm\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - if (!StringUtils.isEmpty(startPriority)) { - cmdXml.append("" + startPriority + "\r\n"); - } - if (!StringUtils.isEmpty(endPriority)) { - cmdXml.append("" + endPriority + "\r\n"); - } - if (!StringUtils.isEmpty(alarmMethod)) { - cmdXml.append("" + alarmMethod + "\r\n"); - } - if (!StringUtils.isEmpty(alarmType)) { - cmdXml.append("" + alarmType + "\r\n"); - } - if (!StringUtils.isEmpty(startTime)) { - cmdXml.append("" + startTime + "\r\n"); - } - if (!StringUtils.isEmpty(endTime)) { - cmdXml.append("" + endTime + "\r\n"); - } - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, expires, "presence" , callIdHeader); - transmitRequest(device, request); - - return true; - - } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) { - e.printStackTrace(); - return false; - } - } - - @Override - public boolean catalogSubscribe(Device device, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) { - try { - StringBuffer cmdXml = new StringBuffer(200); - cmdXml.append("\r\n"); - cmdXml.append("\r\n"); - cmdXml.append("Catalog\r\n"); - cmdXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - cmdXml.append("" + device.getDeviceId() + "\r\n"); - cmdXml.append("\r\n"); - - String tm = Long.toString(System.currentTimeMillis()); - - CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), "z9hG4bK-viaPos-" + tm, "fromTagPos" + tm, null, device.getSubscribeCycleForCatalog(), "Catalog" , callIdHeader); - transmitRequest(device, request, errorEvent, okEvent); - - return true; - - } catch ( NumberFormatException | ParseException | InvalidArgumentException | SipException e) { - e.printStackTrace(); - return false; - } - } - - - private ClientTransaction transmitRequest(Device device, Request request) throws SipException { - return transmitRequest(device, request, null, null); - } - - private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent) throws SipException { - return transmitRequest(device, request, errorEvent, null); - } - - private ClientTransaction transmitRequest(Device device, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException { - ClientTransaction clientTransaction = null; - if("TCP".equals(device.getTransport())) { - clientTransaction = tcpSipProvider.getNewClientTransaction(request); - } else if("UDP".equals(device.getTransport())) { - clientTransaction = udpSipProvider.getNewClientTransaction(request); - } - - CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME); - // 添加错误订阅 - if (errorEvent != null) { - sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> { - errorEvent.response(eventResult); - sipSubscribe.removeErrorSubscribe(eventResult.callId); - })); - } - // 添加订阅 - if (okEvent != null) { - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult ->{ - okEvent.response(eventResult); - sipSubscribe.removeOkSubscribe(eventResult.callId); - }); - } - - clientTransaction.sendRequest(); - return clientTransaction; - } - - /** - * 回放暂停 - */ - @Override - public void playPauseCmd(Device device, StreamInfo streamInfo) { - try { - Long cseq = redisCatchStorage.getCSEQ(Request.INFO); - StringBuffer content = new StringBuffer(200); - content.append("PAUSE RTSP/1.0\r\n"); - content.append("CSeq: " + cseq + "\r\n"); - content.append("PauseTime: now\r\n"); - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString(), cseq); - logger.info(request.toString()); - ClientTransaction clientTransaction = null; - if ("TCP".equals(device.getTransport())) { - clientTransaction = tcpSipProvider.getNewClientTransaction(request); - } else if ("UDP".equals(device.getTransport())) { - clientTransaction = udpSipProvider.getNewClientTransaction(request); - } - if (clientTransaction != null) { - clientTransaction.sendRequest(); - } - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - /** - * 回放恢复 - */ - @Override - public void playResumeCmd(Device device, StreamInfo streamInfo) { - try { - Long cseq = redisCatchStorage.getCSEQ(Request.INFO); - StringBuffer content = new StringBuffer(200); - content.append("PLAY RTSP/1.0\r\n"); - content.append("CSeq: " + cseq + "\r\n"); - content.append("Range: npt=now-\r\n"); - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString(), cseq); - logger.info(request.toString()); - ClientTransaction clientTransaction = null; - if ("TCP".equals(device.getTransport())) { - clientTransaction = tcpSipProvider.getNewClientTransaction(request); - } else if ("UDP".equals(device.getTransport())) { - clientTransaction = udpSipProvider.getNewClientTransaction(request); - } - - clientTransaction.sendRequest(); - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - /** - * 回放拖动播放 - */ - @Override - public void playSeekCmd(Device device, StreamInfo streamInfo, long seekTime) { - try { - Long cseq = redisCatchStorage.getCSEQ(Request.INFO); - StringBuffer content = new StringBuffer(200); - content.append("PLAY RTSP/1.0\r\n"); - content.append("CSeq: " + cseq + "\r\n"); - content.append("Range: npt=" + Math.abs(seekTime) + "-\r\n"); - - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString(), cseq); - logger.info(request.toString()); - ClientTransaction clientTransaction = null; - if ("TCP".equals(device.getTransport())) { - clientTransaction = tcpSipProvider.getNewClientTransaction(request); - } else if ("UDP".equals(device.getTransport())) { - clientTransaction = udpSipProvider.getNewClientTransaction(request); - } - - clientTransaction.sendRequest(); - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } - - /** - * 回放倍速播放 - */ - @Override - public void playSpeedCmd(Device device, StreamInfo streamInfo, Double speed) { - try { - Long cseq = redisCatchStorage.getCSEQ(Request.INFO); - StringBuffer content = new StringBuffer(200); - content.append("PLAY RTSP/1.0\r\n"); - content.append("CSeq: " + cseq + "\r\n"); - content.append("Scale: " + String.format("%.1f",speed) + "\r\n"); - Request request = headerProvider.createInfoRequest(device, streamInfo, content.toString(), cseq); - logger.info(request.toString()); - ClientTransaction clientTransaction = null; - if ("TCP".equals(device.getTransport())) { - clientTransaction = tcpSipProvider.getNewClientTransaction(request); - } else if ("UDP".equals(device.getTransport())) { - clientTransaction = udpSipProvider.getNewClientTransaction(request); - } - - clientTransaction.sendRequest(); - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - } - } + StringBuilder builder = new StringBuilder("A50F01"); + String strTmp; + strTmp = String.format("%02X", cmdCode); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", parameter1); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", parameter2); + builder.append(strTmp, 0, 2); + strTmp = String.format("%X", combineCode2); + builder.append(strTmp, 0, 1).append("0"); + //计算校验码 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + parameter1 + parameter2 + (combineCode2 & 0XF0)) % 0X100; + strTmp = String.format("%02X", checkCode); + builder.append(strTmp, 0, 2); + return builder.toString(); + } + + /** + * 云台控制,支持方向与缩放控制 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param moveSpeed 镜头移动速度 + * @param zoomSpeed 镜头缩放速度 + */ + @Override + public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, + int zoomSpeed) throws InvalidArgumentException, SipException, ParseException { + String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed); + StringBuilder ptzXml = new StringBuilder(200); + String charset = device.getCharset(); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("DeviceControl\r\n"); + ptzXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + ptzXml.append("" + channelId + "\r\n"); + ptzXml.append("" + cmdStr + "\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("5\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request); + } + + /** + * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令 + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param cmdCode 指令码 + * @param parameter1 数据1 + * @param parameter2 数据2 + * @param combineCode2 组合码2 + */ + @Override + public void frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2) throws SipException, InvalidArgumentException, ParseException { + + String cmdStr = frontEndCmdString(cmdCode, parameter1, parameter2, combineCode2); + StringBuffer ptzXml = new StringBuffer(200); + String charset = device.getCharset(); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("DeviceControl\r\n"); + ptzXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + ptzXml.append("" + channelId + "\r\n"); + ptzXml.append("" + cmdStr + "\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("5\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + + + + + SIPRequest request = (SIPRequest) headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request); + + } + + /** + * 前端控制指令(用于转发上级指令) + * + * @param device 控制设备 + * @param channelId 预览通道 + * @param cmdString 前端控制指令串 + */ + @Override + public void fronEndCmd(Device device, String channelId, String cmdString, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer ptzXml = new StringBuffer(200); + String charset = device.getCharset(); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("DeviceControl\r\n"); + ptzXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + ptzXml.append("" + channelId + "\r\n"); + ptzXml.append("" + cmdString + "\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("5\r\n"); + ptzXml.append("\r\n"); + ptzXml.append("\r\n"); + + + Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request, errorEvent, okEvent); + + } + + /** + * 请求预览视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param event hook订阅 + * @param errorEvent sip错误订阅 + */ + @Override + public void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + ZlmHttpHookSubscribe.Event event, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + String stream = ssrcInfo.getStream(); + + if (device == null) { + return; + } + + logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> { + if (event != null) { + event.response(mediaServerItemInUse, json); + subscribe.removeSubscribe(hookSubscribe); + } + }); + String sdpIp; + if (!ObjectUtils.isEmpty(device.getSdpIp())) { + sdpIp = device.getSdpIp(); + }else { + sdpIp = mediaServerItem.getSdpIp(); + } + 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=Play\r\n"); + content.append("c=IN IP4 " + sdpIp + "\r\n"); + content.append("t=0 0\r\n"); + + if (userSetting.isSeniorSdp()) { + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } else { + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("UDP".equalsIgnoreCase(device.getStreamMode())) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } + + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc + // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率 +// content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备 + + + + Request request = headerProvider.createInviteRequest(device, channelId, 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(), channelId, ssrcInfo.getStream()); + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + errorEvent.response(e); + }), e -> { + // 这里为例避免一个通道的点播只有一个callID这个参数使用一个固定值 + ResponseEvent responseEvent = (ResponseEvent) e.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + streamSession.put(device.getDeviceId(), channelId, "play", stream, ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.play); + okEvent.response(e); + }); + } + + /** + * 请求回放视频流 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + */ + @Override + public void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + String startTime, String endTime, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent, + SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); + String sdpIp; + if (!ObjectUtils.isEmpty(device.getSdpIp())) { + sdpIp = device.getSdpIp(); + }else { + sdpIp = mediaServerItem.getSdpIp(); + } + 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=Playback\r\n"); + content.append("u=" + channelId + ":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"); + + String streamMode = device.getStreamMode(); + + if (userSetting.isSeniorSdp()) { + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("UDP".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } else { + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("UDP".equalsIgnoreCase(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { + // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { + // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } + + //ssrc + content.append("y=" + ssrcInfo.getSsrc() + "\r\n"); + + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + // 添加订阅 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> { + if (hookEvent != null) { + InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()); + hookEvent.call(inviteStreamInfo); + } + subscribe.removeSubscribe(hookSubscribe); + }); + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, 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, VideoStreamSessionManager.SessionType.playback); + okEvent.response(event); + }); + if (inviteStreamCallback != null) { + inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream())); + } + } + + /** + * 请求历史媒体下载 + * + * @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(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent, + SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort()); + String sdpIp; + if (!ObjectUtils.isEmpty(device.getSdpIp())) { + sdpIp = device.getSdpIp(); + }else { + sdpIp = mediaServerItem.getSdpIp(); + } + 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=Download\r\n"); + content.append("u=" + channelId + ":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"); + + String streamMode = device.getStreamMode().toUpperCase(); + + if (userSetting.isSeniorSdp()) { + if ("TCP-PASSIVE".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); + } else if ("UDP".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 126 125 99 34 98 97\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=fmtp:126 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:126 H264/90000\r\n"); + content.append("a=rtpmap:125 H264S/90000\r\n"); + content.append("a=fmtp:125 profile-level-id=42e01e\r\n"); + content.append("a=rtpmap:99 MP4V-ES/90000\r\n"); + content.append("a=fmtp:99 profile-level-id=3\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } else { + if ("TCP-PASSIVE".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 97 98 99\r\n"); + } else if ("UDP".equals(streamMode)) { + content.append("m=video " + ssrcInfo.getPort() + " RTP/AVP 96 97 98 99\r\n"); + } + content.append("a=recvonly\r\n"); + content.append("a=rtpmap:96 PS/90000\r\n"); + content.append("a=rtpmap:97 MPEG4/90000\r\n"); + content.append("a=rtpmap:98 H264/90000\r\n"); + content.append("a=rtpmap:99 H265/90000\r\n"); + if ("TCP-PASSIVE".equals(streamMode)) { // tcp被动模式 + content.append("a=setup:passive\r\n"); + content.append("a=connection:new\r\n"); + } else if ("TCP-ACTIVE".equals(streamMode)) { // tcp主动模式 + content.append("a=setup:active\r\n"); + content.append("a=connection:new\r\n"); + } + } + content.append("a=downloadspeed:" + downloadSpeed + "\r\n"); + + content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc + + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId()); + // 添加订阅 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> { + hookEvent.call(new InviteStreamInfo(mediaServerItem, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream())); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("regist", false); + hookSubscribe.getContent().put("schema", "rtsp"); + // 添加流注销的订阅,注销了后向设备发送bye + subscribe.addSubscribe(hookSubscribe, + (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> { + logger.info("[录像]下载结束, 发送BYE"); + try { + streamByeCmd(device, channelId, ssrcInfo.getStream(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId()); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage()); + } + }); + }); + + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc()); + if (inviteStreamCallback != null) { + inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream())); + } + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent -> { + ResponseEvent responseEvent = (ResponseEvent) okEvent.event; + SIPResponse response = (SIPResponse) responseEvent.getResponse(); + streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download); + }); + } + + /** + * 视频流停止, 不使用回调 + */ + @Override + public void streamByeCmd(Device device, String channelId, String stream, String callId) throws InvalidArgumentException, ParseException, SipException, SsrcTransactionNotFoundException { + streamByeCmd(device, channelId, stream, callId, null); + } + + /** + * 视频流停止 + */ + @Override + public void streamByeCmd(Device device, String channelId, String stream, String callId, SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException, SsrcTransactionNotFoundException { + SsrcTransaction ssrcTransaction = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callId, stream); + if (ssrcTransaction == null) { + throw new SsrcTransactionNotFoundException(device.getDeviceId(), channelId, callId, stream); + } + + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); + streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); + + Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo()); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent); + } + + /** + * 语音广播 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + @Override + public void audioBroadcastCmd(Device device, String channelId) { + } + + /** + * 语音广播 + * + * @param device 视频设备 + */ + @Override + public void audioBroadcastCmd(Device device) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer broadcastXml = new StringBuffer(200); + String charset = device.getCharset(); + broadcastXml.append("\r\n"); + broadcastXml.append("\r\n"); + broadcastXml.append("Broadcast\r\n"); + broadcastXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + broadcastXml.append("" + sipConfig.getId() + "\r\n"); + broadcastXml.append("" + device.getDeviceId() + "\r\n"); + broadcastXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + + } + + @Override + public void audioBroadcastCmd(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer broadcastXml = new StringBuffer(200); + String charset = device.getCharset(); + broadcastXml.append("\r\n"); + broadcastXml.append("\r\n"); + broadcastXml.append("Broadcast\r\n"); + broadcastXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + broadcastXml.append("" + sipConfig.getId() + "\r\n"); + broadcastXml.append("" + device.getDeviceId() + "\r\n"); + broadcastXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + + } + + + /** + * 音视频录像控制 + * + * @param device 视频设备 + * @param channelId 预览通道 + * @param recordCmdStr 录像命令:Record / StopRecord + */ + @Override + public void recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("" + recordCmdStr + "\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 远程启动控制命令 + * + * @param device 视频设备 + */ + @Override + public void teleBootCmd(Device device) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("Boot\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + } + + /** + * 报警布防/撤防命令 + * + * @param device 视频设备 + * @param guardCmdStr "SetGuard"/"ResetGuard" + */ + @Override + public void guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("" + guardCmdStr + "\r\n"); + cmdXml.append("\r\n"); + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 报警复位命令 + * + * @param device 视频设备 + */ + @Override + public void alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("ResetAlarm\r\n"); + if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("\r\n"); + } + if (!ObjectUtils.isEmpty(alarmMethod)) { + cmdXml.append("" + alarmMethod + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("" + alarmType + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmMethod) || !ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("\r\n"); + } + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧 + * + * @param device 视频设备 + * @param channelId 预览通道 + */ + @Override + public void iFrameCmd(Device device, String channelId) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("Send\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + } + + /** + * 看守位控制命令 + * + * @param device 视频设备 + * @param enabled 看守位使能:1 = 开启,0 = 关闭 + * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s) + * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255 + */ + @Override + public void homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceControl\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("\r\n"); + if (NumericUtil.isInteger(enabled) && (!enabled.equals("0"))) { + cmdXml.append("1\r\n"); + if (NumericUtil.isInteger(resetTime)) { + cmdXml.append("" + resetTime + "\r\n"); + } else { + cmdXml.append("0\r\n"); + } + if (NumericUtil.isInteger(presetIndex)) { + cmdXml.append("" + presetIndex + "\r\n"); + } else { + cmdXml.append("0\r\n"); + } + } else { + cmdXml.append("0\r\n"); + } + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 设备配置命令 + * + * @param device 视频设备 + */ + @Override + public void deviceConfigCmd(Device device) { + // TODO Auto-generated method stub + } + + /** + * 设备配置命令:basicParam + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param name 设备/通道名称(可选) + * @param expiration 注册过期时间(可选) + * @param heartBeatInterval 心跳间隔时间(可选) + * @param heartBeatCount 心跳超时次数(可选) + */ + @Override + public void deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, + String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("DeviceConfig\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("\r\n"); + if (!ObjectUtils.isEmpty(name)) { + cmdXml.append("" + name + "\r\n"); + } + if (NumericUtil.isInteger(expiration)) { + if (Integer.valueOf(expiration) > 0) { + cmdXml.append("" + expiration + "\r\n"); + } + } + if (NumericUtil.isInteger(heartBeatInterval)) { + if (Integer.valueOf(heartBeatInterval) > 0) { + cmdXml.append("" + heartBeatInterval + "\r\n"); + } + } + if (NumericUtil.isInteger(heartBeatCount)) { + if (Integer.valueOf(heartBeatCount) > 0) { + cmdXml.append("" + heartBeatCount + "\r\n"); + } + } + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询设备状态 + * + * @param device 视频设备 + */ + @Override + public void deviceStatusQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + String charset = device.getCharset(); + StringBuffer catalogXml = new StringBuffer(200); + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + catalogXml.append("DeviceStatus\r\n"); + catalogXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + catalogXml.append("" + device.getDeviceId() + "\r\n"); + catalogXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询设备信息 + * + * @param device 视频设备 + */ + @Override + public void deviceInfoQuery(Device device) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer catalogXml = new StringBuffer(200); + String charset = device.getCharset(); + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + catalogXml.append("DeviceInfo\r\n"); + catalogXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + catalogXml.append("" + device.getDeviceId() + "\r\n"); + catalogXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + + } + + /** + * 查询目录列表 + * + * @param device 视频设备 + */ + @Override + public void catalogQuery(Device device, int sn, SipSubscribe.Event errorEvent) throws SipException, InvalidArgumentException, ParseException { + + StringBuffer catalogXml = new StringBuffer(200); + String charset = device.getCharset(); + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + catalogXml.append(" Catalog\r\n"); + catalogXml.append(" " + sn + "\r\n"); + catalogXml.append(" " + device.getDeviceId() + "\r\n"); + catalogXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询录像信息 + * + * @param device 视频设备 + * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss + * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss + */ + @Override + public void recordInfoQuery(Device device, String channelId, String startTime, String endTime, int sn, Integer secrecy, String type, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + if (secrecy == null) { + secrecy = 0; + } + if (type == null) { + type = "all"; + } + + StringBuffer recordInfoXml = new StringBuffer(200); + String charset = device.getCharset(); + recordInfoXml.append("\r\n"); + recordInfoXml.append("\r\n"); + recordInfoXml.append("RecordInfo\r\n"); + recordInfoXml.append("" + sn + "\r\n"); + recordInfoXml.append("" + channelId + "\r\n"); + if (startTime != null) { + recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(startTime) + "\r\n"); + } + if (endTime != null) { + recordInfoXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(endTime) + "\r\n"); + } + if (secrecy != null) { + recordInfoXml.append(" " + secrecy + " \r\n"); + } + if (type != null) { + // 大华NVR要求必须增加一个值为all的文本元素节点Type + recordInfoXml.append("" + type + "\r\n"); + } + recordInfoXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(), + SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); + } + + /** + * 查询报警信息 + * + * @param device 视频设备 + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethod 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + @Override + public void alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod, String alarmType, + String startTime, String endTime, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("Alarm\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + if (!ObjectUtils.isEmpty(startPriority)) { + cmdXml.append("" + startPriority + "\r\n"); + } + if (!ObjectUtils.isEmpty(endPriority)) { + cmdXml.append("" + endPriority + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmMethod)) { + cmdXml.append("" + alarmMethod + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("" + alarmType + "\r\n"); + } + if (!ObjectUtils.isEmpty(startTime)) { + cmdXml.append("" + startTime + "\r\n"); + } + if (!ObjectUtils.isEmpty(endTime)) { + cmdXml.append("" + endTime + "\r\n"); + } + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询设备配置 + * + * @param device 视频设备 + * @param channelId 通道编码(可选) + * @param configType 配置类型: + */ + @Override + public void deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("ConfigDownload\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("" + configType + "\r\n"); + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询设备预置位置 + * + * @param device 视频设备 + */ + @Override + public void presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("PresetQuery\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + cmdXml.append("" + device.getDeviceId() + "\r\n"); + } else { + cmdXml.append("" + channelId + "\r\n"); + } + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + } + + /** + * 查询移动设备位置数据 + * + * @param device 视频设备 + */ + @Override + public void mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer mobilePostitionXml = new StringBuffer(200); + String charset = device.getCharset(); + mobilePostitionXml.append("\r\n"); + mobilePostitionXml.append("\r\n"); + mobilePostitionXml.append("MobilePosition\r\n"); + mobilePostitionXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + mobilePostitionXml.append("" + device.getDeviceId() + "\r\n"); + mobilePostitionXml.append("60\r\n"); + mobilePostitionXml.append("\r\n"); + + + + Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent); + + } + + /** + * 订阅、取消订阅移动位置 + * + * @param device 视频设备 + * @return true = 命令发送成功 + */ + @Override + public SIPRequest mobilePositionSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer subscribePostitionXml = new StringBuffer(200); + String charset = device.getCharset(); + subscribePostitionXml.append("\r\n"); + subscribePostitionXml.append("\r\n"); + subscribePostitionXml.append("MobilePosition\r\n"); + subscribePostitionXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + subscribePostitionXml.append("" + device.getDeviceId() + "\r\n"); + if (device.getSubscribeCycleForMobilePosition() > 0) { + subscribePostitionXml.append("" + device.getMobilePositionSubmissionInterval() + "\r\n"); + } + subscribePostitionXml.append("\r\n"); + + CallIdHeader callIdHeader; + + if (requestOld != null) { + callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId()); + } else { + callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()); + } + SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), requestOld, device.getSubscribeCycleForMobilePosition(), "presence",callIdHeader); //Position;id=" + tm.substring(tm.length() - 4)); + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); + return request; + } + + /** + * 订阅、取消订阅报警信息 + * + * @param device 视频设备 + * @param expires 订阅过期时间(0 = 取消订阅) + * @param startPriority 报警起始级别(可选) + * @param endPriority 报警终止级别(可选) + * @param alarmMethod 报警方式条件(可选) + * @param alarmType 报警类型 + * @param startTime 报警发生起始时间(可选) + * @param endTime 报警发生终止时间(可选) + * @return true = 命令发送成功 + */ + @Override + public void alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("Alarm\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + if (!ObjectUtils.isEmpty(startPriority)) { + cmdXml.append("" + startPriority + "\r\n"); + } + if (!ObjectUtils.isEmpty(endPriority)) { + cmdXml.append("" + endPriority + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmMethod)) { + cmdXml.append("" + alarmMethod + "\r\n"); + } + if (!ObjectUtils.isEmpty(alarmType)) { + cmdXml.append("" + alarmType + "\r\n"); + } + if (!ObjectUtils.isEmpty(startTime)) { + cmdXml.append("" + startTime + "\r\n"); + } + if (!ObjectUtils.isEmpty(endTime)) { + cmdXml.append("" + endTime + "\r\n"); + } + cmdXml.append("\r\n"); + + + + Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), null, expires, "presence",sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request); + + } + + @Override + public SIPRequest catalogSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer cmdXml = new StringBuffer(200); + String charset = device.getCharset(); + cmdXml.append("\r\n"); + cmdXml.append("\r\n"); + cmdXml.append("Catalog\r\n"); + cmdXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + cmdXml.append("" + device.getDeviceId() + "\r\n"); + cmdXml.append("\r\n"); + + CallIdHeader callIdHeader; + + if (requestOld != null) { + callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId()); + } else { + callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()); + } + + // 有效时间默认为60秒以上 + SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, cmdXml.toString(), requestOld, device.getSubscribeCycleForCatalog(), "Catalog", + callIdHeader); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); + return request; + } + + @Override + public void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException { + + StringBuffer dragXml = new StringBuffer(200); + String charset = device.getCharset(); + dragXml.append("\r\n"); + dragXml.append("\r\n"); + dragXml.append("DeviceControl\r\n"); + dragXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + if (ObjectUtils.isEmpty(channelId)) { + dragXml.append("" + device.getDeviceId() + "\r\n"); + } else { + dragXml.append("" + channelId + "\r\n"); + } + dragXml.append(cmdString); + 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()); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request); + } + + + + + + /** + * 回放暂停 + */ + @Override + public void playPauseCmd(Device device, 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); + } + + + /** + * 回放恢复 + */ + @Override + public void playResumeCmd(Device device, 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); + } + + /** + * 回放拖动播放 + */ + @Override + public void playSeekCmd(Device device, 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); + } + + /** + * 回放倍速播放 + */ + @Override + public void playSpeedCmd(Device device, 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); + } + + private int getInfoCseq() { + return (int) ((Math.random() * 9 + 1) * Math.pow(10, 8)); + } + + @Override + public void playbackControlCmd(Device device, 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()); + if (ssrcTransaction == null) { + logger.info("[回放控制]未找到视频流信息,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream()); + return; + } + + SIPRequest request = headerProvider.createInfoRequest(device, streamInfo.getChannelId(), content.toString(), ssrcTransaction.getSipTransactionInfo()); + if (request == null) { + logger.info("[回放控制]构建Request信息失败,设备:{}, 流ID: {}", device.getDeviceId(), streamInfo.getStream()); + return; + } + + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); + } + + @Override + public void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException { + if (device == null) { + return; + } + logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(), + deviceAlarm.getLongitude(), deviceAlarm.getLatitude()); + + String characterSet = device.getCharset(); + StringBuffer deviceStatusXml = new StringBuffer(600); + deviceStatusXml.append("\r\n"); + deviceStatusXml.append("\r\n"); + deviceStatusXml.append("Alarm\r\n"); + deviceStatusXml.append("" + (int) ((Math.random() * 9 + 1) * 100000) + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getChannelId() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getAlarmPriority() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getAlarmMethod() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getAlarmTime() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getAlarmDescription() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getLongitude() + "\r\n"); + deviceStatusXml.append("" + deviceAlarm.getLatitude() + "\r\n"); + deviceStatusXml.append("\r\n"); + deviceStatusXml.append("" + deviceAlarm.getAlarmType() + "\r\n"); + deviceStatusXml.append("\r\n"); + deviceStatusXml.append("\r\n"); + + + Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request); + + + } } 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/SIPCommanderFroPlatform.java index 65348ab6..6efdafe3 100644 --- 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/SIPCommanderFroPlatform.java @@ -1,27 +1,39 @@ package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; +import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.conf.DynamicTask; +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.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; +import com.genersoft.iot.vmp.utils.DateUtil; +import gov.nist.javax.sip.message.MessageFactoryImpl; +import gov.nist.javax.sip.message.SIPRequest; 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.context.annotation.DependsOn; -import org.springframework.context.annotation.Lazy; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; -import javax.sip.*; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; import javax.sip.header.CallIdHeader; import javax.sip.header.WWWAuthenticateHeader; import javax.sip.message.Request; import java.text.ParseException; -import java.util.UUID; +import java.util.ArrayList; +import java.util.List; @Component @DependsOn("sipLayer") @@ -29,219 +41,248 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { private final Logger logger = LoggerFactory.getLogger(SIPCommanderFroPlatform.class); - // @Autowired - // private SipConfig sipConfig; - - // @Autowired - // private SIPRequestHeaderProvider headerProvider; - @Autowired - private SIPRequestHeaderPlarformProvider headerProviderPlarformProvider; - - // @Autowired - // private VideoStreamSessionManager streamSession; - - // @Autowired - // private IVideoManagerStorager storager; + private SIPRequestHeaderPlarformProvider headerProviderPlatformProvider; @Autowired private IRedisCatchStorage redisCatchStorage; + @Autowired + private IMediaServerService mediaServerService; + @Autowired private SipSubscribe sipSubscribe; - @Lazy @Autowired - @Qualifier(value="tcpSipProvider") - private SipProvider tcpSipProvider; + private ZLMRTPServerFactory zlmrtpServerFactory; - @Lazy @Autowired - @Qualifier(value="udpSipProvider") - private SipProvider udpSipProvider; + private SipLayer sipLayer; + + @Autowired + private SIPSender sipSender; + + @Autowired + private DynamicTask dynamicTask; @Override - public boolean register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) { - return register(parentPlatform, null, null, errorEvent, okEvent); + public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + register(parentPlatform, null, null, errorEvent, okEvent, false, true); } @Override - public boolean unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) { - parentPlatform.setExpires("0"); - ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); - if (parentPlatformCatch != null) { - parentPlatformCatch.setParentPlatform(parentPlatform); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - } - - return register(parentPlatform, null, null, errorEvent, okEvent); + public void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException { + register(parentPlatform, null, null, errorEvent, okEvent, false, false); } @Override - public boolean register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) { - try { - Request request = null; - String tm = Long.toString(System.currentTimeMillis()); - if (www == null ) { - // //callid - CallIdHeader callIdHeader = null; - if(parentPlatform.getTransport().equals("TCP")) { - callIdHeader = tcpSipProvider.getNewCallId(); - } - if(parentPlatform.getTransport().equals("UDP")) { - callIdHeader = udpSipProvider.getNewCallId(); - } + public void register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www, + SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException { + Request request; + if (!registerAgain ) { + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); - request = headerProviderPlarformProvider.createRegisterRequest(parentPlatform, 1L, "FromRegister" + tm, null, callIdHeader); + request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, + redisCatchStorage.getCSEQ(), SipUtils.getNewFromTag(), + SipUtils.getNewViaTag(), callIdHeader, isRegister); // 将 callid 写入缓存, 等注册成功可以更新状态 - redisCatchStorage.updatePlatformRegisterInfo(callIdHeader.getCallId(), parentPlatform.getServerGBId()); + String callIdFromHeader = callIdHeader.getCallId(); + redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister)); sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (event)->{ if (event != null) { - logger.info("向上级平台 [ {} ] 注册发上错误: {} ", + logger.info("向上级平台 [ {} ] 注册发生错误: {} ", parentPlatform.getServerGBId(), event.msg); } + redisCatchStorage.delPlatformRegisterInfo(callIdFromHeader); if (errorEvent != null ) { errorEvent.response(event); } }); }else { - CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - request = headerProviderPlarformProvider.createRegisterRequest(parentPlatform, "FromRegister" + tm, null, callId, www, callIdHeader); + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); + request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, SipUtils.getNewFromTag(), null, callId, www, callIdHeader, isRegister); } - transmitRequest(parentPlatform, request, null, okEvent); - return true; - } catch (ParseException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (PeerUnavailableException e) { - e.printStackTrace(); - } catch (SipException e) { - e.printStackTrace(); - } - return false; + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent); } @Override - public String keepalive(ParentPlatform parentPlatform) { - String callId = null; - try { - + public String keepalive(ParentPlatform parentPlatform,SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException { + String characterSet = parentPlatform.getCharacterSet(); StringBuffer keepaliveXml = new StringBuffer(200); - keepaliveXml.append("\r\n"); - keepaliveXml.append("\r\n"); - keepaliveXml.append("Keepalive\r\n"); - keepaliveXml.append("" + (int)((Math.random()*9+1)*100000) + "\r\n"); - keepaliveXml.append("" + parentPlatform.getDeviceGBId() + "\r\n"); - keepaliveXml.append("OK\r\n"); - keepaliveXml.append("\r\n"); + keepaliveXml.append("\r\n") + .append("\r\n") + .append("Keepalive\r\n") + .append("" + (int)((Math.random()*9+1)*100000) + "\r\n") + .append("" + parentPlatform.getDeviceGBId() + "\r\n") + .append("OK\r\n") + .append("\r\n"); - CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); - Request request = headerProviderPlarformProvider.createKeetpaliveMessageRequest( + Request request = headerProviderPlatformProvider.createMessageRequest( parentPlatform, keepaliveXml.toString(), - "z9hG4bK-" + UUID.randomUUID().toString().replace("-", ""), - UUID.randomUUID().toString().replace("-", ""), - null, + SipUtils.getNewFromTag(), + SipUtils.getNewViaTag(), callIdHeader); - transmitRequest(parentPlatform, request); - callId = callIdHeader.getCallId(); - } catch (ParseException | InvalidArgumentException | SipException e) { - e.printStackTrace(); - } - return callId; - } - - private void transmitRequest(ParentPlatform parentPlatform, Request request) throws SipException { - transmitRequest(parentPlatform, request, null, null); - } - - private void transmitRequest(ParentPlatform parentPlatform, Request request, SipSubscribe.Event errorEvent) throws SipException { - transmitRequest(parentPlatform, request, errorEvent, null); - } - - private void transmitRequest(ParentPlatform parentPlatform, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException { - if("TCP".equals(parentPlatform.getTransport())) { - tcpSipProvider.sendRequest(request); - - } else if("UDP".equals(parentPlatform.getTransport())) { - udpSipProvider.sendRequest(request); - } - - CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME); - // 添加错误订阅 - if (errorEvent != null) { - sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), errorEvent); - } - // 添加订阅 - if (okEvent != null) { - sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent); - } - + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, errorEvent, okEvent); + return callIdHeader.getCallId(); } /** * 向上级回复通道信息 * @param channel 通道信息 * @param parentPlatform 平台信息 - * @return */ @Override - public boolean catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) { + public void catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) throws SipException, InvalidArgumentException, ParseException { if ( parentPlatform ==null) { - return false; + return ; } - try { - StringBuffer catalogXml = new StringBuffer(600); - catalogXml.append("\r\n"); - catalogXml.append("\r\n"); - catalogXml.append("Catalog\r\n"); - catalogXml.append("" +sn + "\r\n"); - catalogXml.append("" + parentPlatform.getDeviceGBId() + "\r\n"); - catalogXml.append("" + size + "\r\n"); - catalogXml.append("\r\n"); - catalogXml.append("\r\n"); - if (channel != null) { + List channels = new ArrayList<>(); + if (channel != null) { + channels.add(channel); + } + String catalogXml = getCatalogXml(channels, sn, parentPlatform, size); + + // callid + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); + + Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + + } + + @Override + public void catalogQuery(List channels, ParentPlatform 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(); + 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("" + 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"); - catalogXml.append("" + channel.getManufacture() + "\r\n"); - catalogXml.append("" + channel.getModel() + "\r\n"); - catalogXml.append("" + channel.getOwner() + "\r\n"); - catalogXml.append("" + channel.getCivilCode() + "\r\n"); - catalogXml.append("
" + channel.getAddress() + "
\r\n"); - catalogXml.append("" + channel.getParental() + "\r\n");// TODO 当前不能添加分组, 所以暂时没有父节点 - catalogXml.append("" + channel.getParentId() + "\r\n"); // TODO 当前不能添加分组, 所以暂时没有父节点 - catalogXml.append("" + channel.getSecrecy() + "\r\n"); - catalogXml.append("" + channel.getRegisterWay() + "\r\n"); - catalogXml.append("" + (channel.getStatus() == 0?"OFF":"ON") + "\r\n"); - catalogXml.append("\r\n"); + if (channel.getParentId() != null) { + // 业务分组加上这一项即可,提高兼容性, + catalogXml.append("" + channel.getParentId() + "\r\n"); +// catalogXml.append("" + parentPlatform.getDeviceGBId() + "/" + channel.getParentId() + "\r\n"); + } + if (channel.getChannelId().length() == 20 && Integer.parseInt(channel.getChannelId().substring(10, 13)) == 216) { + // 虚拟组织增加BusinessGroupID字段 + catalogXml.append("" + channel.getParentId() + "\r\n"); + } + if (!channel.getChannelId().equals(parentPlatform.getDeviceGBId())) { + catalogXml.append("" + channel.getParental() + "\r\n"); + if (channel.getParental() == 0) { + catalogXml.append("" + (channel.getStatus() == 0 ? "OFF" : "ON") + "\r\n"); + } + } + if (channel.getParental() == 0) { + // 通道项 + catalogXml.append("" + channel.getManufacture() + "\r\n"); + catalogXml.append("" + channel.getSecrecy() + "\r\n"); + catalogXml.append("" + channel.getRegisterWay() + "\r\n"); + String civilCode = channel.getCivilCode() == null?parentPlatform.getAdministrativeDivision() : channel.getCivilCode(); + if (channel.getChannelType() != 2) { // 业务分组/虚拟组织/行政区划 不设置以下属性 + catalogXml.append("" + channel.getModel() + "\r\n"); + catalogXml.append("" + parentPlatform.getDeviceGBId()+ "\r\n"); + catalogXml.append("" + civilCode + "\r\n"); + if (channel.getAddress() == null) { + catalogXml.append("
\r\n"); + }else { + catalogXml.append("
" + channel.getAddress() + "
\r\n"); + } + } + } + catalogXml.append("
\r\n"); } - - - catalogXml.append("
\r\n"); - catalogXml.append("
\r\n"); - catalogXml.append("
\r\n"); - - // callid - CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); - - Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, catalogXml.toString(), fromTag, callIdHeader); - transmitRequest(parentPlatform, request); - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; } - return true; + + catalogXml.append("\r\n"); + catalogXml.append("\r\n"); + return catalogXml.toString(); + } + + private void sendCatalogResponse(List channels, ParentPlatform parentPlatform, String sn, String fromTag, int index, boolean sendAfterResponse) throws SipException, InvalidArgumentException, ParseException { + if (index >= channels.size()) { + return; + } + List deviceChannels; + if (index + parentPlatform.getCatalogGroup() < channels.size()) { + deviceChannels = channels.subList(index, index + parentPlatform.getCatalogGroup()); + }else { + deviceChannels = channels.subList(index, channels.size()); + } + String catalogXml = getCatalogXml(deviceChannels, sn, parentPlatform, channels.size()); + // callid + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); + + SIPRequest request = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader); + + String timeoutTaskKey = "catalog_task_" + parentPlatform.getServerGBId() + sn; + + String callId = request.getCallIdHeader().getCallId(); + + 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); + dynamicTask.stop(timeoutTaskKey); + }, eventResult -> { + dynamicTask.stop(timeoutTaskKey); + int indexNext = index + parentPlatform.getCatalogGroup(); + try { + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, true); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); + } + }); + }else { + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, eventResult -> { + logger.error("[目录推送失败] 国标级联 platform : {}, code: {}, msg: {}, 停止发送", parentPlatform.getServerGBId(), eventResult.statusCode, eventResult.msg); + dynamicTask.stop(timeoutTaskKey); + }, null); + dynamicTask.startDelay(timeoutTaskKey, ()->{ + int indexNext = index + parentPlatform.getCatalogGroup(); + try { + sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext, false); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); + } + }, 30); + } } /** @@ -252,35 +293,28 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { * @return */ @Override - public boolean deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) { + public void deviceInfoResponse(ParentPlatform parentPlatform,Device device, String sn, String fromTag) throws SipException, InvalidArgumentException, ParseException { if (parentPlatform == null) { - return false; + return; } - try { - StringBuffer deviceInfoXml = new StringBuffer(600); - deviceInfoXml.append("\r\n"); - deviceInfoXml.append("\r\n"); - deviceInfoXml.append("DeviceInfo\r\n"); - deviceInfoXml.append("" +sn + "\r\n"); - deviceInfoXml.append("" + parentPlatform.getDeviceGBId() + "\r\n"); - deviceInfoXml.append("" + parentPlatform.getName() + "\r\n"); - deviceInfoXml.append("wvp\r\n"); - deviceInfoXml.append("wvp-28181-2.0\r\n"); - deviceInfoXml.append("2.0.202107\r\n"); - deviceInfoXml.append("OK\r\n"); - deviceInfoXml.append("\r\n"); + String characterSet = parentPlatform.getCharacterSet(); + StringBuffer deviceInfoXml = new StringBuffer(600); + deviceInfoXml.append("\r\n"); + deviceInfoXml.append("\r\n"); + deviceInfoXml.append("DeviceInfo\r\n"); + deviceInfoXml.append("" +sn + "\r\n"); + deviceInfoXml.append("" + device.getDeviceId() + "\r\n"); + deviceInfoXml.append("" + device.getName() + "\r\n"); + deviceInfoXml.append("" + device.getManufacturer() + "\r\n"); + deviceInfoXml.append("" + device.getModel() + "\r\n"); + deviceInfoXml.append("" + device.getFirmware() + "\r\n"); + deviceInfoXml.append("OK\r\n"); + deviceInfoXml.append("\r\n"); - CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); - Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, deviceInfoXml.toString(), fromTag, callIdHeader); - transmitRequest(parentPlatform, request); - - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; - } - return true; + Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceInfoXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); } /** @@ -291,32 +325,362 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform { * @return */ @Override - public boolean deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag) { + public void deviceStatusResponse(ParentPlatform parentPlatform,String channelId, String sn, String fromTag,int status) throws SipException, InvalidArgumentException, ParseException { if (parentPlatform == null) { - return false; + return ; } - try { - StringBuffer deviceStatusXml = new StringBuffer(600); - deviceStatusXml.append("\r\n"); - deviceStatusXml.append("\r\n"); - deviceStatusXml.append("DeviceStatus\r\n"); - deviceStatusXml.append("" +sn + "\r\n"); - deviceStatusXml.append("" + parentPlatform.getDeviceGBId() + "\r\n"); - deviceStatusXml.append("OK\r\n"); - deviceStatusXml.append("ONLINE\r\n"); - deviceStatusXml.append("OK\r\n"); - deviceStatusXml.append("\r\n"); + String statusStr = (status==1)?"ONLINE":"OFFLINE"; + String characterSet = parentPlatform.getCharacterSet(); + StringBuffer deviceStatusXml = new StringBuffer(600); + deviceStatusXml.append("\r\n") + .append("\r\n") + .append("DeviceStatus\r\n") + .append("" +sn + "\r\n") + .append("" + channelId + "\r\n") + .append("OK\r\n") + .append(""+statusStr+"\r\n") + .append("OK\r\n") + .append("\r\n"); - CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId() - : udpSipProvider.getNewCallId(); + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); - Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), fromTag, callIdHeader); - transmitRequest(parentPlatform, request); + Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + } - } catch (SipException | ParseException | InvalidArgumentException e) { - e.printStackTrace(); - return false; + @Override + public void sendNotifyMobilePosition(ParentPlatform parentPlatform, GPSMsgInfo gpsMsgInfo, SubscribeInfo subscribeInfo) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { + if (parentPlatform == null) { + return; } - return true; + if (logger.isDebugEnabled()) { + logger.debug("[发送 移动位置订阅] {}/{}->{},{}", parentPlatform.getServerGBId(), gpsMsgInfo.getId(), gpsMsgInfo.getLng(), gpsMsgInfo.getLat()); + } + + String characterSet = parentPlatform.getCharacterSet(); + StringBuffer deviceStatusXml = new StringBuffer(600); + deviceStatusXml.append("\r\n") + .append("\r\n") + .append("MobilePosition\r\n") + .append("" + (int)((Math.random()*9+1)*100000) + "\r\n") + .append("" + gpsMsgInfo.getId() + "\r\n") + .append("\r\n") + .append("" + gpsMsgInfo.getLng() + "\r\n") + .append("" + gpsMsgInfo.getLat() + "\r\n") + .append("" + gpsMsgInfo.getSpeed() + "\r\n") + .append("" + gpsMsgInfo.getDirection() + "\r\n") + .append("" + gpsMsgInfo.getAltitude() + "\r\n") + .append("\r\n"); + + sendNotify(parentPlatform, deviceStatusXml.toString(), subscribeInfo, eventResult -> { + logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); + }, null); + + } + + @Override + public void sendAlarmMessage(ParentPlatform parentPlatform, DeviceAlarm deviceAlarm) throws SipException, InvalidArgumentException, ParseException { + if (parentPlatform == null) { + return; + } + logger.info("[发送报警通知] {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(), + deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm)); + String characterSet = parentPlatform.getCharacterSet(); + StringBuffer deviceStatusXml = new StringBuffer(600); + deviceStatusXml.append("\r\n") + .append("\r\n") + .append("Alarm\r\n") + .append("" + (int)((Math.random()*9+1)*100000) + "\r\n") + .append("" + deviceAlarm.getChannelId() + "\r\n") + .append("" + deviceAlarm.getAlarmPriority() + "\r\n") + .append("" + deviceAlarm.getAlarmMethod() + "\r\n") + .append("" + deviceAlarm.getAlarmTime() + "\r\n") + .append("" + deviceAlarm.getAlarmDescription() + "\r\n") + .append("" + deviceAlarm.getLongitude() + "\r\n") + .append("" + deviceAlarm.getLatitude() + "\r\n") + .append("\r\n") + .append("" + deviceAlarm.getAlarmType() + "\r\n") + .append("\r\n") + .append("\r\n"); + + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); + + Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), SipUtils.getNewFromTag(), SipUtils.getNewViaTag(), callIdHeader); + sipSender.transmitRequest(parentPlatform.getDeviceIp(), request); + + } + + @Override + public void sendNotifyForCatalogAddOrUpdate(String type, ParentPlatform parentPlatform, List deviceChannels, SubscribeInfo subscribeInfo, Integer index) throws InvalidArgumentException, ParseException, NoSuchFieldException, SipException, IllegalAccessException { + if (parentPlatform == null || deviceChannels == null || deviceChannels.size() == 0 || subscribeInfo == null) { + return; + } + if (index == null) { + index = 0; + } + if (index >= deviceChannels.size()) { + return; + } + List channels; + if (index + parentPlatform.getCatalogGroup() < deviceChannels.size()) { + channels = deviceChannels.subList(index, index + parentPlatform.getCatalogGroup()); + }else { + channels = deviceChannels.subList(index, deviceChannels.size()); + } + Integer finalIndex = index; + String catalogXmlContent = getCatalogXmlContentForCatalogAddOrUpdate(parentPlatform, channels, + deviceChannels.size(), type, subscribeInfo); + sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> { + logger.error("发送NOTIFY通知消息失败。错误:{} {}", eventResult.statusCode, eventResult.msg); + }, (eventResult -> { + try { + sendNotifyForCatalogAddOrUpdate(type, parentPlatform, deviceChannels, subscribeInfo, + finalIndex + parentPlatform.getCatalogGroup()); + } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | + IllegalAccessException e) { + logger.error("[命令发送失败] 国标级联 NOTIFY通知: {}", e.getMessage()); + } + })); + } + + private void sendNotify(ParentPlatform parentPlatform, String catalogXmlContent, + SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent ) + throws SipException, ParseException, InvalidArgumentException { + MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory(); + String characterSet = parentPlatform.getCharacterSet(); + // 设置编码, 防止中文乱码 + messageFactory.setDefaultContentEncodingCharset(characterSet); + + SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, catalogXmlContent, subscribeInfo); + + sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest); + } + + private String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List channels, int sumNum, String type, SubscribeInfo subscribeInfo) { + StringBuffer catalogXml = new StringBuffer(600); + + String characterSet = parentPlatform.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("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"); + // 行政区划分组只需要这两项就可以 + 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.getStatus() == 0 ? "OFF" : "ON") + "\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"); + } + if (!"presence".equals(subscribeInfo.getEventType())) { + catalogXml.append("" + type + "\r\n"); + } + + } + catalogXml.append("
\r\n"); + } + } + catalogXml.append("
\r\n") + .append("
\r\n"); + return catalogXml.toString(); + } + + @Override + public void sendNotifyForCatalogOther(String type, ParentPlatform 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("[缺少必要参数]"); + return; + } + + if (index == null) { + index = 0; + } + if (index >= deviceChannels.size()) { + return; + } + List channels; + if (index + parentPlatform.getCatalogGroup() < deviceChannels.size()) { + channels = deviceChannels.subList(index, index + parentPlatform.getCatalogGroup()); + }else { + channels = deviceChannels.subList(index, deviceChannels.size()); + } + Integer finalIndex = index; + String catalogXmlContent = getCatalogXmlContentForCatalogOther(parentPlatform, channels, type); + sendNotify(parentPlatform, catalogXmlContent, subscribeInfo, eventResult -> { + logger.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()); + } + }); + } + + private String getCatalogXmlContentForCatalogOther(ParentPlatform parentPlatform, List channels, String type) { + + String characterSet = parentPlatform.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("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"); + } + } + catalogXml.append("\r\n") + .append("\r\n"); + return catalogXml.toString(); + } + @Override + public void recordInfo(DeviceChannel deviceChannel, ParentPlatform parentPlatform, String fromTag, RecordInfo recordInfo) throws SipException, InvalidArgumentException, ParseException { + if ( parentPlatform ==null) { + return ; + } + 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("" + recordInfo.getDeviceId() + "\r\n") + .append("" + recordInfo.getSumNum() + "\r\n"); + if (recordInfo.getRecordList() == null ) { + recordXml.append("\r\n"); + }else { + recordXml.append("\r\n"); + if (recordInfo.getRecordList().size() > 0) { + for (RecordItem recordItem : recordInfo.getRecordList()) { + recordXml.append("\r\n"); + if (deviceChannel != null) { + recordXml.append("" + recordItem.getDeviceId() + "\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") + .append("" + recordItem.getSecrecy() + "\r\n") + .append("" + recordItem.getType() + "\r\n"); + if (!ObjectUtils.isEmpty(recordItem.getFileSize())) { + recordXml.append("" + recordItem.getFileSize() + "\r\n"); + } + if (!ObjectUtils.isEmpty(recordItem.getFilePath())) { + recordXml.append("" + recordItem.getFilePath() + "\r\n"); + } + } + recordXml.append("\r\n"); + } + } + } + + recordXml.append("\r\n") + .append("\r\n"); + + // 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); + + } + + @Override + public void sendMediaStatusNotify(ParentPlatform parentPlatform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException { + if (sendRtpItem == null || parentPlatform == null) { + return; + } + + + String characterSet = parentPlatform.getCharacterSet(); + StringBuffer mediaStatusXml = new StringBuffer(200); + mediaStatusXml.append("\r\n") + .append("\r\n") + .append("MediaStatus\r\n") + .append("" + (int)((Math.random()*9+1)*100000) + "\r\n") + .append("" + sendRtpItem.getChannelId() + "\r\n") + .append("121\r\n") + .append("\r\n"); + + SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, mediaStatusXml.toString(), + sendRtpItem); + + sipSender.transmitRequest(parentPlatform.getDeviceIp(),messageRequest); + + } + + @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 void streamByeCmd(ParentPlatform parentPlatform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException { + if (sendRtpItem == null ) { + logger.info("[向上级发送BYE], sendRtpItem 为NULL"); + return; + } + if (parentPlatform == null) { + logger.info("[向上级发送BYE], platform 为NULL"); + return; + } + logger.info("[向上级发送BYE], {}/{}", parentPlatform.getServerGBId(), sendRtpItem.getChannelId()); + String mediaServerId = sendRtpItem.getMediaServerId(); + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem != null) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc()); + zlmrtpServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId()); + } + SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(parentPlatform, sendRtpItem); + if (byeRequest == null) { + logger.warn("[向上级发送bye]:无法创建 byeRequest"); + } + sipSender.transmitRequest(parentPlatform.getDeviceIp(),byeRequest); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java deleted file mode 100644 index dd098f7b..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorAbstract.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.event.request; - -import gov.nist.javax.sip.SipProviderImpl; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - -/** - * @description:处理接收IPCamera发来的SIP协议请求消息 - * @author: songww - * @date: 2020年5月3日 下午4:42:22 - */ -public abstract class SIPRequestProcessorAbstract { - - - @Autowired - @Qualifier(value="tcpSipProvider") - private SipProviderImpl tcpSipProvider; - - @Autowired - @Qualifier(value="udpSipProvider") - private SipProviderImpl udpSipProvider; - -} 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 d6d3dd70..55c98feb 100644 --- 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,9 +1,11 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request; -import gov.nist.javax.sip.SipProviderImpl; -import gov.nist.javax.sip.SipStackImpl; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import gov.nist.javax.sip.message.SIPRequest; -import gov.nist.javax.sip.stack.SIPServerTransaction; +import gov.nist.javax.sip.message.SIPResponse; +import org.apache.commons.lang3.ArrayUtils; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; @@ -11,20 +13,21 @@ import org.dom4j.io.SAXReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import javax.sip.*; import javax.sip.address.Address; -import javax.sip.address.AddressFactory; import javax.sip.address.SipURI; import javax.sip.header.ContentTypeHeader; +import javax.sip.header.ExpiresHeader; import javax.sip.header.HeaderFactory; -import javax.sip.header.ViaHeader; import javax.sip.message.MessageFactory; import javax.sip.message.Request; import javax.sip.message.Response; import java.io.ByteArrayInputStream; import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * @description:处理接收IPCamera发来的SIP协议请求消息 @@ -36,61 +39,7 @@ public abstract class SIPRequestProcessorParent { private final static Logger logger = LoggerFactory.getLogger(SIPRequestProcessorParent.class); @Autowired - @Qualifier(value="tcpSipProvider") - private SipProviderImpl tcpSipProvider; - - @Autowired - @Qualifier(value="udpSipProvider") - private SipProviderImpl udpSipProvider; - - /** - * 根据 RequestEvent 获取 ServerTransaction - * @param evt - * @return - */ - public ServerTransaction getServerTransaction(RequestEvent evt) { - Request request = evt.getRequest(); - ServerTransaction serverTransaction = evt.getServerTransaction(); - // 判断TCP还是UDP - boolean isTcp = false; - ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); - String transport = reqViaHeader.getTransport(); - if (transport.equals("TCP")) { - isTcp = true; - } - - if (serverTransaction == null) { - try { - if (isTcp) { - SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack(); - serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true); - if (serverTransaction == null) { - serverTransaction = tcpSipProvider.getNewServerTransaction(request); - } - } else { - SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack(); - serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true); - if (serverTransaction == null) { - serverTransaction = udpSipProvider.getNewServerTransaction(request); - } - } - } catch (TransactionAlreadyExistsException e) { - logger.error(e.getMessage()); - } catch (TransactionUnavailableException e) { - logger.error(e.getMessage()); - } - } - return serverTransaction; - } - - public AddressFactory getAddressFactory() { - try { - return SipFactory.getInstance().createAddressFactory(); - } catch (PeerUnavailableException e) { - e.printStackTrace(); - } - return null; - } + private SIPSender sipSender; public HeaderFactory getHeaderFactory() { try { @@ -110,69 +59,147 @@ public abstract class SIPRequestProcessorParent { return null; } + class ResponseAckExtraParam{ + String content; + ContentTypeHeader contentTypeHeader; + SipURI sipURI; + int expires = -1; + } + /*** * 回复状态码 * 100 trying * 200 OK * 400 * 404 - * @param evt - * @throws SipException - * @throws InvalidArgumentException - * @throws ParseException */ - public void responseAck(RequestEvent evt, int statusCode) throws SipException, InvalidArgumentException, ParseException { - Response response = getMessageFactory().createResponse(statusCode, evt.getRequest()); - ServerTransaction serverTransaction = getServerTransaction(evt); - serverTransaction.sendResponse(response); - if (statusCode >= 200 && !"NOTIFY".equals(evt.getRequest().getMethod())) { - - if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete(); - } + public SIPResponse responseAck(SIPRequest sipRequest, int statusCode) throws SipException, InvalidArgumentException, ParseException { + return responseAck(sipRequest, statusCode, null); } - public void responseAck(RequestEvent evt, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException { - Response response = getMessageFactory().createResponse(statusCode, evt.getRequest()); - response.setReasonPhrase(msg); - ServerTransaction serverTransaction = getServerTransaction(evt); - serverTransaction.sendResponse(response); - if (statusCode >= 200 && !"NOTIFY".equals(evt.getRequest().getMethod())) { - if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete(); + public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException { + return responseAck(sipRequest, statusCode, msg, null); + } + + public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg, ResponseAckExtraParam responseAckExtraParam) throws SipException, InvalidArgumentException, ParseException { + if (sipRequest.getToHeader().getTag() == null) { + sipRequest.getToHeader().setTag(SipUtils.getNewTag()); } + SIPResponse response = (SIPResponse)getMessageFactory().createResponse(statusCode, sipRequest); + response.setStatusCode(statusCode); + if (msg != null) { + response.setReasonPhrase(msg); + } + + if (responseAckExtraParam != null) { + if (responseAckExtraParam.sipURI != null && sipRequest.getMethod().equals(Request.INVITE)) { + logger.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() + )); + response.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); + } + if (responseAckExtraParam.contentTypeHeader != null) { + response.setContent(responseAckExtraParam.content, responseAckExtraParam.contentTypeHeader); + } + + if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) { + if (responseAckExtraParam.expires == -1) { + logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); + }else { + ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(responseAckExtraParam.expires); + response.addHeader(expiresHeader); + } + } + }else { + if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) { + logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header"); + } + } + + // 发送response + sipSender.transmitRequest(sipRequest.getLocalAddress().getHostAddress(), response); + + return response; } /** * 回复带sdp的200 - * @param evt - * @param sdp - * @throws SipException - * @throws InvalidArgumentException - * @throws ParseException */ - public void responseAck(RequestEvent evt, String sdp) throws SipException, InvalidArgumentException, ParseException { - Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest()); - SipFactory sipFactory = SipFactory.getInstance(); - ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); - response.setContent(sdp, contentTypeHeader); + public SIPResponse responseSdpAck(SIPRequest request, String sdp, ParentPlatform platform) throws SipException, InvalidArgumentException, ParseException { - SipURI sipURI = (SipURI)evt.getRequest().getRequestURI(); + ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); - Address concatAddress = sipFactory.createAddressFactory().createAddress( - sipFactory.createAddressFactory().createSipURI(sipURI.getUser(), sipURI.getHost()+":"+sipURI.getPort() - )); - response.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress)); - getServerTransaction(evt).sendResponse(response); + // 兼容国标中的使用编码@域名作为RequestURI的情况 + SipURI sipURI = (SipURI)request.getRequestURI(); + if (sipURI.getPort() == -1) { + sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+":"+platform.getServerPort()); + } + ResponseAckExtraParam responseAckExtraParam = new ResponseAckExtraParam(); + responseAckExtraParam.contentTypeHeader = contentTypeHeader; + responseAckExtraParam.content = sdp; + responseAckExtraParam.sipURI = sipURI; + + return responseAck(request, Response.OK, null, responseAckExtraParam); + } + + /** + * 回复带xml的200 + */ + public SIPResponse responseXmlAck(SIPRequest request, String xml, ParentPlatform 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()); + } + ResponseAckExtraParam responseAckExtraParam = new ResponseAckExtraParam(); + responseAckExtraParam.contentTypeHeader = contentTypeHeader; + responseAckExtraParam.content = xml; + responseAckExtraParam.sipURI = sipURI; + responseAckExtraParam.expires = expires; + return responseAck(request, Response.OK, null, responseAckExtraParam); } public Element getRootElement(RequestEvent evt) throws DocumentException { return getRootElement(evt, "gb2312"); } public Element getRootElement(RequestEvent evt, String charset) throws DocumentException { - if (charset == null) charset = "gb2312"; + if (charset == null) { + charset = "gb2312"; + } Request request = evt.getRequest(); SAXReader reader = new SAXReader(); reader.setEncoding(charset); - Document xml = reader.read(new ByteArrayInputStream(request.getRawContent())); + // 对海康出现的未转义字符做处理。 + String[] destStrArray = new String[]{"<",">","&","'","""}; + char despChar = '&'; // 或许可扩展兼容其他字符 + byte destBye = (byte) despChar; + List result = new ArrayList<>(); + byte[] rawContent = request.getRawContent(); + if (rawContent == null) { + return null; + } + for (int i = 0; i < rawContent.length; i++) { + if (rawContent[i] == destBye) { + boolean resul = false; + for (String destStr : destStrArray) { + if (i + destStr.length() <= rawContent.length) { + byte[] bytes = Arrays.copyOfRange(rawContent, i, i + destStr.length()); + resul = resul || (Arrays.equals(bytes,destStr.getBytes())); + } + } + if (resul) { + result.add(rawContent[i]); + } + }else { + result.add(rawContent[i]); + } + } + Byte[] bytes = new Byte[0]; + byte[] bytesResult = ArrayUtils.toPrimitive(result.toArray(bytes)); + + Document xml = reader.read(new ByteArrayInputStream(bytesResult)); return xml.getRootElement(); } 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 127ef29a..7a26e783 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 @@ -1,27 +1,38 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; -import com.genersoft.iot.vmp.common.StreamInfo; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; 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.zlm.ZLMRTPServerFactory; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.bean.RequestPushStreamMsg; +import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; 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.Dialog; -import javax.sip.DialogState; +import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; +import javax.sip.SipException; import javax.sip.address.SipURI; +import javax.sip.header.CallIdHeader; import javax.sip.header.FromHeader; import javax.sip.header.HeaderAddress; import javax.sip.header.ToHeader; +import java.text.ParseException; import java.util.HashMap; import java.util.Map; @@ -32,7 +43,7 @@ import java.util.Map; public class AckRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class); - private String method = "ACK"; + private final String method = "ACK"; @Autowired private SIPProcessorObserver sipProcessorObserver; @@ -46,12 +57,33 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In @Autowired private IRedisCatchStorage redisCatchStorage; + @Autowired + private IVideoManagerStorage storager; + @Autowired private ZLMRTPServerFactory zlmrtpServerFactory; + @Autowired + private ZlmHttpHookSubscribe hookSubscribe; + @Autowired private IMediaServerService mediaServerService; + @Autowired + private ZlmHttpHookSubscribe subscribe; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ISIPCommander cmder; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Autowired + private RedisGbPlayMsgListener redisGbPlayMsgListener; + /** * 处理 ACK请求 @@ -60,62 +92,79 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In */ @Override public void process(RequestEvent evt) { - Dialog dialog = evt.getDialog(); - if (dialog == null) return; - if (dialog.getState()== DialogState.CONFIRMED) { - String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId); - String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; - String deviceId = sendRtpItem.getDeviceId(); - StreamInfo streamInfo = null; - if (deviceId == null) { - streamInfo = new StreamInfo(); - streamInfo.setApp(sendRtpItem.getApp()); - streamInfo.setStreamId(sendRtpItem.getStreamId()); - }else { - streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); - sendRtpItem.setStreamId(streamInfo.getStreamId()); - streamInfo.setApp("rtp"); - } + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); - redisCatchStorage.updateSendRTPSever(sendRtpItem); - logger.info(platformGbId); - logger.info(channelId); - Map param = new HashMap<>(); - param.put("vhost","__defaultVhost__"); - param.put("app",streamInfo.getApp()); - param.put("stream",streamInfo.getStreamId()); - param.put("ssrc", sendRtpItem.getSsrc()); - param.put("dst_url",sendRtpItem.getIp()); - param.put("dst_port", sendRtpItem.getPort()); - param.put("is_udp", is_Udp); - //param.put ("src_port", sendRtpItem.getLocalPort()); - // 设备推流查询,成功后才能转推 - boolean rtpPushed = false; - long startTime = System.currentTimeMillis(); - while (!rtpPushed) { + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); + logger.info("[收到ACK]: platformGbId->{}", platformGbId); + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformGbId); + // 取消设置的超时任务 + dynamicTask.stop(callIdHeader.getCallId()); + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId()); + if (sendRtpItem == null) { + logger.warn("[收到ACK]:未找到通道({})的推流信息", channelId); + return; + } + String is_Udp = sendRtpItem.isTcp() ? "0" : "1"; + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + logger.info("收到ACK,rtp/{}开始向上级推流, 目标={}:{},SSRC={}", sendRtpItem.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc()); + Map param = new HashMap<>(12); + param.put("vhost","__defaultVhost__"); + param.put("app",sendRtpItem.getApp()); + param.put("stream",sendRtpItem.getStreamId()); + param.put("ssrc", sendRtpItem.getSsrc()); + param.put("dst_url",sendRtpItem.getIp()); + param.put("dst_port", sendRtpItem.getPort()); + param.put("is_udp", is_Udp); + param.put("src_port", sendRtpItem.getLocalPort()); + param.put("pt", sendRtpItem.getPt()); + param.put("use_ps", sendRtpItem.isUsePs() ? "1" : "0"); + param.put("only_audio", sendRtpItem.isOnlyAudio() ? "1" : "0"); + if (!sendRtpItem.isTcp()) { + // 开启rtcp保活 + param.put("udp_rtcp_timeout", sendRtpItem.isRtcp()? "1":"0"); + } + + if (mediaInfo == null) { + RequestPushStreamMsg requestPushStreamMsg = RequestPushStreamMsg.getInstance( + sendRtpItem.getMediaServerId(), sendRtpItem.getApp(), sendRtpItem.getStreamId(), + sendRtpItem.getIp(), sendRtpItem.getPort(), sendRtpItem.getSsrc(), sendRtpItem.isTcp(), + sendRtpItem.getLocalPort(), sendRtpItem.getPt(), sendRtpItem.isUsePs(), sendRtpItem.isOnlyAudio()); + redisGbPlayMsgListener.sendMsgForStartSendRtpStream(sendRtpItem.getServerId(), requestPushStreamMsg, jsonObject->{ + startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, jsonObject, param, callIdHeader); + }); + }else { + // 如果是非严格模式,需要关闭端口占用 + JSONObject startSendRtpStreamResult = null; + if (sendRtpItem.getLocalPort() != 0) { + if (zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc())) { + startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); + } + }else { + startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); + } + if (startSendRtpStreamResult != null) { + startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader); + } + } + } + private void startSendRtpStreamHand(RequestEvent evt, SendRtpItem sendRtpItem, ParentPlatform parentPlatform, + JSONObject jsonObject, Map param, CallIdHeader callIdHeader) { + if (jsonObject == null) { + logger.error("RTP推流失败: 请检查ZLM服务"); + } else if (jsonObject.getInteger("code") == 0) { + logger.info("调用ZLM推流接口, 结果: {}", jsonObject); + logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port")); + } else { + logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param)); + if (sendRtpItem.isOnlyAudio()) { + // TODO 可能是语音对讲 + }else { + // 向上级平台 try { - if (System.currentTimeMillis() - startTime < 30 * 1000) { - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) { - rtpPushed = true; - logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]", - streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort()); - zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); - } else { - logger.info("等待设备推流[{}/{}].......", - streamInfo.getApp() ,streamInfo.getStreamId()); - Thread.sleep(1000); - continue; - } - } else { - rtpPushed = true; - logger.info("设备推流[{}/{}]超时,终止向上级推流", - streamInfo.getApp() ,streamInfo.getStreamId()); - } - } catch (InterruptedException e) { - e.printStackTrace(); + commanderForPlatform.streamByeCmd(parentPlatform, callIdHeader.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); } } } 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 feb44c54..addd6336 100644 --- 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 @@ -1,17 +1,24 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamType; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; 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.zlm.ZLMRTPServerFactory; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.IDeviceService; import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; @@ -20,6 +27,7 @@ import org.springframework.stereotype.Component; import javax.sip.*; import javax.sip.address.SipURI; +import javax.sip.header.CallIdHeader; import javax.sip.header.FromHeader; import javax.sip.header.HeaderAddress; import javax.sip.header.ToHeader; @@ -44,7 +52,10 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In private IRedisCatchStorage redisCatchStorage; @Autowired - private IVideoManagerStorager storager; + private IDeviceService deviceService; + + @Autowired + private IVideoManagerStorage storager; @Autowired private ZLMRTPServerFactory zlmrtpServerFactory; @@ -55,6 +66,9 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In @Autowired private SIPProcessorObserver sipProcessorObserver; + @Autowired + private VideoStreamSessionManager streamSession; + @Override public void afterPropertiesSet() throws Exception { // 添加消息处理的订阅 @@ -67,48 +81,82 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In */ @Override public void process(RequestEvent evt) { + try { - responseAck(evt, Response.OK); - Dialog dialog = evt.getDialog(); - if (dialog == null) return; - if (dialog.getState().equals(DialogState.TERMINATED)) { - String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); - String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); - SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId); - logger.info("收到bye, [{}/{}]", platformGbId, channelId); - if (sendRtpItem != null){ - String streamId = sendRtpItem.getStreamId(); - Map param = new HashMap<>(); - param.put("vhost","__defaultVhost__"); - param.put("app",sendRtpItem.getApp()); - param.put("stream",streamId); - param.put("ssrc",sendRtpItem.getSsrc()); - logger.info("停止向上级推流:" + streamId); - MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); - zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); - redisCatchStorage.deleteSendRTPServer(platformGbId, channelId); - if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) { - logger.info(streamId + "无其它观看者,通知设备停止推流"); - cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId); + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[回复BYE信息失败],{}", e.getMessage()); + } + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); + String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser(); + String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(platformGbId, channelId, null, callIdHeader.getCallId()); + logger.info("[收到bye] {}/{}", platformGbId, channelId); + if (sendRtpItem != null){ + String streamId = sendRtpItem.getStreamId(); + Map param = new HashMap<>(); + param.put("vhost","__defaultVhost__"); + param.put("app",sendRtpItem.getApp()); + param.put("stream",streamId); + param.put("ssrc",sendRtpItem.getSsrc()); + logger.info("[收到bye] 停止向上级推流:{}", streamId); + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + redisCatchStorage.deleteSendRTPServer(platformGbId, channelId, callIdHeader.getCallId(), null); + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); + int totalReaderCount = zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId); + if (totalReaderCount <= 0) { + logger.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); + if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { + Device device = deviceService.getDevice(sendRtpItem.getDeviceId()); + if (device == null) { + logger.info("[收到bye] {} 通知设备停止推流时未找到设备信息", streamId); + } + try { + logger.warn("[停止点播] {}/{}", sendRtpItem.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, streamId, null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[收到bye] {} 无其它观看者,通知设备停止推流, 发送BYE失败 {}",streamId, e.getMessage()); + } } - } - // 可能是设备主动停止 - Device device = storager.queryVideoDeviceByChannelId(platformGbId); - if (device != null) { - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId); - if (streamInfo != null) { - redisCatchStorage.stopPlay(streamInfo); + if (sendRtpItem.getPlayType().equals(InviteStreamType.PUSH)) { + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(0, + sendRtpItem.getApp(), sendRtpItem.getStreamId(), sendRtpItem.getChannelId(), + sendRtpItem.getPlatformId(), null, null, sendRtpItem.getMediaServerId()); + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); } - storager.stopPlay(device.getDeviceId(), channelId); - mediaServerService.closeRTPServer(device, channelId); } } - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } + // 可能是设备主动停止 + Device device = storager.queryVideoDeviceByChannelId(platformGbId); + if (device != null) { + storager.stopPlay(device.getDeviceId(), channelId); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId); + if (streamInfo != null) { + redisCatchStorage.stopPlay(streamInfo); + mediaServerService.closeRTPServer(streamInfo.getMediaServerId(), streamInfo.getStream()); + } + SsrcTransaction ssrcTransactionForPlay = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); + if (ssrcTransactionForPlay != null){ + if (ssrcTransactionForPlay.getCallId().equals(callIdHeader.getCallId())){ + // 释放ssrc + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlay.getMediaServerId()); + if (mediaServerItem != null) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlay.getSsrc()); + } + streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlay.getStream()); + } + } + SsrcTransaction ssrcTransactionForPlayBack = streamSession.getSsrcTransaction(device.getDeviceId(), channelId, callIdHeader.getCallId(), null); + if (ssrcTransactionForPlayBack != null) { + // 释放ssrc + MediaServerItem mediaServerItem = mediaServerService.getOne(ssrcTransactionForPlayBack.getMediaServerId()); + if (mediaServerItem != null) { + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcTransactionForPlayBack.getSsrc()); + } + streamSession.remove(device.getDeviceId(), channelId, ssrcTransactionForPlayBack.getStream()); + } + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java index 0a818ee6..b04352a6 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/CancelRequestProcessor.java @@ -15,7 +15,7 @@ import javax.sip.RequestEvent; @Component public class CancelRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private String method = "CANCEL"; + private final String method = "CANCEL"; @Autowired private SIPProcessorObserver sipProcessorObserver; 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 aee414f5..d320ac52 100644 --- 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,20 +1,36 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; 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.zlm.ZLMMediaListManager; import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.*; import com.genersoft.iot.vmp.service.IMediaServerService; 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.service.bean.MessageForPushChannel; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import com.genersoft.iot.vmp.service.redisMsg.RedisGbPlayMsgListener; +import com.genersoft.iot.vmp.service.redisMsg.RedisPushStreamResponseListener; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; -import gov.nist.javax.sip.address.AddressImpl; -import gov.nist.javax.sip.address.SipUri; +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.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; @@ -24,14 +40,12 @@ import org.springframework.stereotype.Component; import javax.sdp.*; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; -import javax.sip.ServerTransaction; import javax.sip.SipException; -import javax.sip.address.SipURI; -import javax.sip.header.FromHeader; -import javax.sip.message.Request; +import javax.sip.header.CallIdHeader; import javax.sip.message.Response; import java.text.ParseException; -import java.util.List; +import java.time.Instant; +import java.util.Random; import java.util.Vector; /** @@ -41,357 +55,878 @@ import java.util.Vector; @Component public class InviteRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class); + private final static Logger logger = LoggerFactory.getLogger(InviteRequestProcessor.class); - private String method = "INVITE"; + private final String method = "INVITE"; - @Autowired - private SIPCommanderFroPlatform cmderFroPlatform; + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; - @Autowired - private IVideoManagerStorager storager; + @Autowired + private IVideoManagerStorage storager; - @Autowired - private IRedisCatchStorage redisCatchStorage; + @Autowired + private IStreamPushService streamPushService; + @Autowired + private IStreamProxyService streamProxyService; - @Autowired - private SIPCommander cmder; + @Autowired + private IRedisCatchStorage redisCatchStorage; - @Autowired - private IPlayService playService; + @Autowired + private DynamicTask dynamicTask; - @Autowired - private ZLMRTPServerFactory zlmrtpServerFactory; + @Autowired + private RedisPushStreamResponseListener redisPushStreamResponseListener; - @Autowired - private IMediaServerService mediaServerService; + @Autowired + private IPlayService playService; - @Autowired - private SIPProcessorObserver sipProcessorObserver; + @Autowired + private SIPSender sipSender; - @Override - public void afterPropertiesSet() throws Exception { - // 添加消息处理的订阅 - sipProcessorObserver.addRequestProcessor(method, this); - } + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; - /** - * 处理invite请求 - * - * @param evt - * 请求消息 - */ - @Override - public void process(RequestEvent evt) { - // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令 - try { - Request request = evt.getRequest(); - SipURI sipURI = (SipURI) request.getRequestURI(); - String channelId = sipURI.getUser(); - String requesterId = null; + @Autowired + private IMediaServerService mediaServerService; - FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); - AddressImpl address = (AddressImpl) fromHeader.getAddress(); - SipUri uri = (SipUri) address.getURI(); - requesterId = uri.getUser(); + @Autowired + private ZlmHttpHookSubscribe zlmHttpHookSubscribe; - if (requesterId == null || channelId == null) { - logger.info("无法从FromHeader的Address中获取到平台id,返回400"); - responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误 - return; - } + @Autowired + private SIPProcessorObserver sipProcessorObserver; - // 查询请求方是否上级平台 - ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); - if (platform != null) { - // 查询平台下是否有该通道 - DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); - List gbStreams = storager.queryStreamInParentPlatform(requesterId, channelId); - GbStream gbStream = gbStreams.size() > 0? gbStreams.get(0):null; - MediaServerItem mediaServerItem = null; - // 不是通道可能是直播流 - if (channel != null && gbStream == null ) { - if (channel.getStatus() == 0) { - logger.info("通道离线,返回400"); - responseAck(evt, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline"); - return; - } - responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中 - }else if(channel == null && gbStream != null){ - String mediaServerId = gbStream.getMediaServerId(); - mediaServerItem = mediaServerService.getOne(mediaServerId); - if (mediaServerItem == null) { - logger.info("[ app={}, stream={} ]找不到zlm {},返回410",gbStream.getApp(), gbStream.getStream(), mediaServerId); - responseAck(evt, Response.GONE, "media server not found"); - return; - } - Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); - if (!streamReady ) { - logger.info("[ app={}, stream={} ]通道离线,返回400",gbStream.getApp(), gbStream.getStream()); - responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline"); - return; - } - responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中 - }else { - logger.info("通道不存在,返回404"); - responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在 - return; - } - // 解析sdp消息, 使用jainsip 自带的sdp解析方式 - String contentString = new String(request.getRawContent()); + @Autowired + private VideoStreamSessionManager sessionManager; - // jainSip不支持y=字段, 移除以解析。 - int ssrcIndex = contentString.indexOf("y="); - // 检查是否有y字段 - String ssrcDefault = "0000000000"; - String ssrc; - SessionDescription sdp; - if (ssrcIndex >= 0) { - //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); - String substring = contentString.substring(0, contentString.indexOf("y=")); - sdp = SdpFactory.getInstance().createSessionDescription(substring); - }else { - ssrc = ssrcDefault; - sdp = SdpFactory.getInstance().createSessionDescription(contentString); - } + @Autowired + private UserSetting userSetting; - // 获取支持的格式 - Vector mediaDescriptions = sdp.getMediaDescriptions(true); - // 查看是否支持PS 负载96 - //String ip = null; - int port = -1; - //boolean recvonly = false; - boolean mediaTransmissionTCP = false; - Boolean tcpActive = null; - for (Object description : mediaDescriptions) { - MediaDescription mediaDescription = (MediaDescription) description; - Media media = mediaDescription.getMedia(); + @Autowired + private ZLMMediaListManager mediaListManager; - 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".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; - } - } - if (port == -1) { - logger.info("不支持的媒体格式,返回415"); - // 回复不支持的格式 - responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 - return; - } - String username = sdp.getOrigin().getUsername(); - String addressStr = sdp.getOrigin().getAddress(); - //String sessionName = sdp.getSessionName().getValue(); - logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc); - Device device = null; - // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标 - if (channel != null) { - device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId); - if (device == null) { - logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel); - responseAck(evt, Response.SERVER_INTERNAL_ERROR); - return; - } - mediaServerItem = playService.getNewMediaServerItem(device); - if (mediaServerItem == null) { - logger.warn("未找到可用的zlm"); - responseAck(evt, Response.BUSY_HERE); - return; - } - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, - device.getDeviceId(), channelId, - mediaTransmissionTCP); - if (tcpActive != null) { - sendRtpItem.setTcpActive(tcpActive); - } - if (sendRtpItem == null) { - logger.warn("服务器端口资源不足"); - responseAck(evt, Response.BUSY_HERE); - return; - } + @Autowired + private RedisGbPlayMsgListener redisGbPlayMsgListener; - // 写入redis, 超时时回复 - redisCatchStorage.updateSendRTPSever(sendRtpItem); - // 通知下级推流, - PlayResult playResult = playService.play(mediaServerItem,device.getDeviceId(), channelId, (mediaServerItemInUSe, responseJSON)->{ - // 收到推流, 回复200OK, 等待ack - // if (sendRtpItem == null) return; - sendRtpItem.setStatus(1); - redisCatchStorage.updateSendRTPSever(sendRtpItem); - // TODO 添加对tcp的支持 - StringBuffer content = new StringBuffer(200); - content.append("v=0\r\n"); - content.append("o="+ channelId +" 0 0 IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n"); - content.append("s=Play\r\n"); - content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n"); - content.append("t=0 0\r\n"); - 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="+ ssrc + "\r\n"); - content.append("f=\r\n"); + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } - try { - responseAck(evt, content.toString()); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } - } ,((event) -> { - // 未知错误。直接转发设备点播的错误 - Response response = null; - try { - response = getMessageFactory().createResponse(event.statusCode, evt.getRequest()); - ServerTransaction serverTransaction = getServerTransaction(evt); - serverTransaction.sendResponse(response); - if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete(); - } catch (ParseException | SipException | InvalidArgumentException e) { - e.printStackTrace(); - } - })); - if (logger.isDebugEnabled()) { - logger.debug(playResult.getResult().toString()); - } + /** + * 处理invite请求 + * + * @param evt 请求消息 + */ + @Override + public void process(RequestEvent evt) { + // Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令 + try { + SIPRequest request = (SIPRequest)evt.getRequest(); + String channelId = SipUtils.getChannelIdFromRequest(request); + String requesterId = SipUtils.getUserIdFromFromHeader(request); + CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); + if (requesterId == null || channelId == null) { + logger.info("无法从FromHeader的Address中获取到平台id,返回400"); + // 参数不全, 发400,请求错误 + try { + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); + } + return; + } - }else if (gbStream != null) { - SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, - gbStream.getApp(), gbStream.getStream(), channelId, - mediaTransmissionTCP); - if (tcpActive != null) { - sendRtpItem.setTcpActive(tcpActive); - } - if (sendRtpItem == null) { - logger.warn("服务器端口资源不足"); - responseAck(evt, Response.BUSY_HERE); - return; - } + // 查询请求是否来自上级平台\设备 + ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId); + if (platform == null) { + inviteFromDeviceHandle(request, requesterId); - // 写入redis, 超时时回复 - redisCatchStorage.updateSendRTPSever(sendRtpItem); + } else { + // 查询平台下是否有该通道 + DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId); + GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId); + PlatformCatalog catalog = storager.getCatalog(channelId); - sendRtpItem.setStatus(1); - redisCatchStorage.updateSendRTPSever(sendRtpItem); - // TODO 添加对tcp的支持 - 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("s=Play\r\n"); - content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n"); - content.append("t=0 0\r\n"); - 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="+ ssrc + "\r\n"); - content.append("f=\r\n"); + MediaServerItem 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) { - try { - responseAck(evt, content.toString()); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } - } + 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 || streamPushItem.getServerId().equals(userSetting.getServerId())) { + 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 ("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); + try { + // 通道不存在,发404,资源不存在 + responseAck(request, Response.NOT_FOUND); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 通道不存在: {}", e.getMessage()); + } + return; + } + // 解析sdp消息, 使用jainsip 自带的sdp解析方式 + String contentString = new String(request.getRawContent()); - } else { - // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) - Device device = redisCatchStorage.getDevice(requesterId); - if (device != null) { - logger.info("收到设备" + requesterId + "的语音广播Invite请求"); - responseAck(evt, Response.TRYING); + // jainSip不支持y=字段, 移除以解析。 + int ssrcIndex = contentString.indexOf("y="); + // 检查是否有y字段 + String ssrcDefault = "0000000000"; + String ssrc; + SessionDescription sdp; + if (ssrcIndex >= 0) { + //ssrc规定长度为10个字节,不取余下长度以避免后续还有“f=”字段 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + String substring = contentString.substring(0, contentString.indexOf("y=")); + sdp = SdpFactory.getInstance().createSessionDescription(substring); + } else { + ssrc = ssrcDefault; + sdp = SdpFactory.getInstance().createSessionDescription(contentString); + } + String sessionName = sdp.getSessionName().getValue(); - String contentString = new String(request.getRawContent()); - // jainSip不支持y=字段, 移除移除以解析。 - String substring = contentString; - String ssrc = "0000000404"; - int ssrcIndex = contentString.indexOf("y="); - if (ssrcIndex > 0) { - substring = contentString.substring(0, ssrcIndex); - ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); - } - ssrcIndex = substring.indexOf("f="); - if (ssrcIndex > 0) { - substring = contentString.substring(0, ssrcIndex); - } - SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); + 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(); - // 获取支持的格式 - Vector mediaDescriptions = sdp.getMediaDescriptions(true); - // 查看是否支持PS 负载96 - int port = -1; - //boolean recvonly = false; - boolean mediaTransmissionTCP = false; - Boolean tcpActive = null; - for (int i = 0; i < mediaDescriptions.size(); i++) { - MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i); - Media media = mediaDescription.getMedia(); + 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("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; - } - } - if (port == -1) { - logger.info("不支持的媒体格式,返回415"); - // 回复不支持的格式 - responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 - return; - } - String username = sdp.getOrigin().getUsername(); - String addressStr = sdp.getOrigin().getAddress(); - logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc); + Vector mediaFormats = media.getMediaFormats(false); + if (mediaFormats.contains("96")) { + port = media.getMediaPort(); + //String mediaType = media.getMediaType(); + String protocol = media.getProtocol(); - } else { - logger.warn("来自无效设备/平台的请求"); - responseAck(evt, Response.BAD_REQUEST); - } - } + // 区分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 = sdp.getOrigin().getAddress(); - } catch (SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); - logger.warn("sdp解析错误"); - e.printStackTrace(); - } catch (SdpParseException e) { - e.printStackTrace(); - } catch (SdpException e) { - e.printStackTrace(); - } - } + logger.info("[上级点播]用户:{}, 通道:{}, 地址:{}:{}, ssrc:{}", username, channelId, addressStr, port, ssrc); + Device device = null; + // 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标 + if (channel != null) { + device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId); + if (device == null) { + logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel); + try { + responseAck(request, Response.SERVER_INTERNAL_ERROR); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.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; + } + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, + device.getDeviceId(), channelId, mediaTransmissionTCP, platform.isRtcp()); + + 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()); + } + return; + } + sendRtpItem.setCallId(callIdHeader.getCallId()); + sendRtpItem.setPlayType("Play".equalsIgnoreCase(sessionName) ? InviteStreamType.PLAY : InviteStreamType.PLAYBACK); + + Long finalStartTime = startTime; + Long finalStopTime = stopTime; + ZlmHttpHookSubscribe.Event hookEvent = (mediaServerItemInUSe, responseJSON) -> { + String app = responseJSON.getString("app"); + String stream = responseJSON.getString("stream"); + logger.info("[上级点播]下级已经开始推流。 回复200OK(SDP), {}/{}", app, stream); + // * 0 等待设备推流上来 + // * 1 下级已经推流,等待上级平台回复ack + // * 2 推流中 + sendRtpItem.setStatus(1); + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\r\n"); + content.append("s=" + sessionName + "\r\n"); + content.append("c=IN IP4 " + mediaServerItemInUSe.getSdpIp() + "\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; + } + 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"); + + 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); + + } catch (SipException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + }; + SipSubscribe.Event errorEvent = ((event) -> { + // 未知错误。直接转发设备点播的错误 + try { + Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest()); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } catch (ParseException | SipException e) { + e.printStackTrace(); + } + }); + sendRtpItem.setApp("rtp"); + if ("Playback".equalsIgnoreCase(sessionName)) { + sendRtpItem.setPlayType(InviteStreamType.PLAYBACK); + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, device.isSsrcCheck(), true); + sendRtpItem.setStreamId(ssrcInfo.getStream()); + // 写入redis, 超时时回复 + redisCatchStorage.updateSendRTPSever(sendRtpItem); + playService.playBack(mediaServerItem, ssrcInfo, device.getDeviceId(), channelId, DateUtil.formatter.format(start), + DateUtil.formatter.format(end), null, result -> { + if (result.getCode() != 0) { + logger.warn("录像回放失败"); + if (result.getEvent() != null) { + errorEvent.response(result.getEvent()); + } + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); + try { + responseAck(request, Response.REQUEST_TIMEOUT); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像回放 发送REQUEST_TIMEOUT: {}", e.getMessage()); + } + } else { + if (result.getMediaServerItem() != null) { + hookEvent.response(result.getMediaServerItem(), result.getResponse()); + } + } + }); + } else { + sendRtpItem.setPlayType(InviteStreamType.PLAY); + SsrcTransaction playTransaction = sessionManager.getSsrcTransaction(device.getDeviceId(), channelId, "play", null); + if (playTransaction != null) { + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, "rtp", playTransaction.getStream()); + if (!streamReady) { + boolean hasRtpServer = mediaServerService.checkRtpServer(mediaServerItem, "rtp", playTransaction.getStream()); + if (hasRtpServer) { + logger.info("[上级点播]已经开启rtpServer但是尚未收到流,开启监听流的到来"); + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", playTransaction.getStream(), true, "rtsp", mediaServerItem.getId()); + zlmHttpHookSubscribe.addSubscribe(hookSubscribe, hookEvent); + }else { + playTransaction = null; + } + } + } + if (playTransaction == null) { + String streamId = null; + if (mediaServerItem.isRtpEnable()) { + streamId = String.format("%s_%s", device.getDeviceId(), channelId); + } + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, null, device.isSsrcCheck(), false); + logger.info(JSONObject.toJSONString(ssrcInfo)); + sendRtpItem.setStreamId(ssrcInfo.getStream()); + sendRtpItem.setSsrc(ssrc.equals(ssrcDefault) ? ssrcInfo.getSsrc() : ssrc); + + // 写入redis, 超时时回复 + redisCatchStorage.updateSendRTPSever(sendRtpItem); + MediaServerItem finalMediaServerItem = mediaServerItem; + playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> { + logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId); + redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null); + }); + } else { + sendRtpItem.setStreamId(playTransaction.getStream()); + // 写入redis, 超时时回复 + redisCatchStorage.updateSendRTPSever(sendRtpItem); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("app", sendRtpItem.getApp()); + jsonObject.put("stream", sendRtpItem.getStreamId()); + hookEvent.response(mediaServerItem, jsonObject); + } + } + } else if (gbStream != null) { + if("push".equals(gbStream.getStreamType())) { + if (streamPushItem != null && streamPushItem.isPushIng()) { + // 推流状态 + pushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } else { + // 未推流 拉起 + notifyStreamOnline(evt, request,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + }else if ("proxy".equals(gbStream.getStreamType())){ + if (null != proxyByAppAndStream) { + if(proxyByAppAndStream.isStatus()){ + pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + }else{ + //开启代理拉流 + notifyStreamOnline(evt, request,gbStream, null, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + } + + + } + } + } + } catch (SdpParseException e) { + logger.error("sdp解析错误", e); + } catch (SdpException e) { + e.printStackTrace(); + } + } + + /** + * 安排推流 + */ + private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + if (streamReady) { + // 自平台内容 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + } + return; + } + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + sendRtpItem.setFromTag(request.getFromTag()); + + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + } + + } + private void pushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + // 推流 + if (streamPushItem.isSelf()) { + Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream()); + if (streamReady) { + // 自平台内容 + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId, + gbStream.getApp(), gbStream.getStream(), channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (sendRtpItem == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage()); + } + return; + } + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + + sendRtpItem.setFromTag(request.getFromTag()); + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + + redisCatchStorage.updateSendRTPSever(sendRtpItem); + + } else { + // 不在线 拉起 + notifyStreamOnline(evt, request,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + + } else { + // 其他平台内容 + otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + } + /** + * 通知流上线 + */ + private void notifyStreamOnline(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + if ("proxy".equals(gbStream.getStreamType())) { + // TODO 控制启用以使设备上线 + logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream()); + // 监听流上线 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(gbStream.getApp(), gbStream.getStream(), true, "rtsp", mediaServerItem.getId()); + zlmHttpHookSubscribe.addSubscribe(hookSubscribe, (mediaServerItemInUSe, responseJSON) -> { + String app = responseJSON.getString("app"); + String stream = responseJSON.getString("stream"); + logger.info("[上级点播]拉流代理已经就绪, {}/{}", app, stream); + dynamicTask.stop(callIdHeader.getCallId()); + pushProxyStream(evt, request, gbStream, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + }); + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { + logger.info("[ app={}, stream={} ] 等待拉流代理流超时", gbStream.getApp(), gbStream.getStream()); + zlmHttpHookSubscribe.removeSubscribe(hookSubscribe); + }, userSetting.getPlatformPlayTimeout()); + boolean start = streamProxyService.start(gbStream.getApp(), gbStream.getStream()); + if (!start) { + try { + responseAck(request, Response.BUSY_HERE, "channel [" + gbStream.getGbId() + "] offline"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); + } + zlmHttpHookSubscribe.removeSubscribe(hookSubscribe); + dynamicTask.stop(callIdHeader.getCallId()); + } + + + + } else if ("push".equals(gbStream.getStreamType())) { + if (!platform.isStartOfflinePush()) { + // 平台设置中关闭了拉起离线的推流则直接回复 + try { + logger.info("[上级点播] 失败,推流设备未推流,channel: {}, app: {}, stream: {}", gbStream.getGbId(), gbStream.getApp(), gbStream.getStream()); + responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage()); + } + return; + } + // 发送redis消息以使设备上线 + logger.info("[ app={}, stream={} ]通道未推流,发送redis信息控制设备开始推流", gbStream.getApp(), gbStream.getStream()); + + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, + gbStream.getApp(), gbStream.getStream(), gbStream.getGbId(), gbStream.getPlatformId(), + platform.getName(), null, gbStream.getMediaServerId()); + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); + // 设置超时 + dynamicTask.startDelay(callIdHeader.getCallId(), () -> { + logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream()); + try { + mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); + responseAck(request, Response.REQUEST_TIMEOUT); // 超时 + } catch (SipException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + }, userSetting.getPlatformPlayTimeout()); + // 添加监听 + int finalPort = port; + Boolean finalTcpActive = tcpActive; + + // 添加在本机上线的通知 + mediaListManager.addChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream(), (app, stream, serverId) -> { + dynamicTask.stop(callIdHeader.getCallId()); + if (serverId.equals(userSetting.getServerId())) { + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, finalPort, ssrc, requesterId, + app, stream, channelId, mediaTransmissionTCP, platform.isRtcp()); + + if (sendRtpItem == null) { + logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + return; + } + if (finalTcpActive != null) { + sendRtpItem.setTcpActive(finalTcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + + sendRtpItem.setFromTag(request.getFromTag()); + SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + redisCatchStorage.updateSendRTPSever(sendRtpItem); + } else { + // 其他平台内容 + otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + }); + + // 添加回复的拒绝或者错误的通知 + redisPushStreamResponseListener.addEvent(gbStream.getApp(), gbStream.getStream(), response -> { + if (response.getCode() != 0) { + dynamicTask.stop(callIdHeader.getCallId()); + mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream()); + try { + responseAck(request, Response.TEMPORARILY_UNAVAILABLE, response.getMsg()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 点播回复: {}", e.getMessage()); + } + } + }); + } + } + + /** + * 来自其他wvp的推流 + */ + private void otherWvpPushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform, + CallIdHeader callIdHeader, MediaServerItem mediaServerItem, + int port, Boolean tcpActive, boolean mediaTransmissionTCP, + String channelId, String addressStr, String ssrc, String requesterId) { + logger.info("[级联点播]直播流来自其他平台,发送redis消息"); + // 发送redis消息 + redisGbPlayMsgListener.sendMsg(streamPushItem.getServerId(), streamPushItem.getMediaServerId(), + streamPushItem.getApp(), streamPushItem.getStream(), addressStr, port, ssrc, requesterId, + channelId, mediaTransmissionTCP, platform.isRtcp(),null, responseSendItemMsg -> { + SendRtpItem sendRtpItem = responseSendItemMsg.getSendRtpItem(); + if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) { + logger.warn("服务器端口资源不足"); + try { + responseAck(request, Response.BUSY_HERE); + } catch (SipException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + return; + } + // 收到sendItem + if (tcpActive != null) { + sendRtpItem.setTcpActive(tcpActive); + } + sendRtpItem.setPlayType(InviteStreamType.PUSH); + // 写入redis, 超时时回复 + sendRtpItem.setStatus(1); + sendRtpItem.setCallId(callIdHeader.getCallId()); + + sendRtpItem.setFromTag(request.getFromTag()); + SIPResponse response = sendStreamAck(responseSendItemMsg.getMediaServerItem(), request,sendRtpItem, platform, evt); + if (response != null) { + sendRtpItem.setToTag(response.getToTag()); + } + redisCatchStorage.updateSendRTPSever(sendRtpItem); + }, (wvpResult) -> { + + // 错误 + if (wvpResult.getCode() == RedisGbPlayMsgListener.ERROR_CODE_OFFLINE) { + // 离线 + // 查询是否在本机上线了 + StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream()); + if (currentStreamPushItem.isPushIng()) { + // 在线状态 + pushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + + } else { + // 不在线 拉起 + notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive, + mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId); + } + } + try { + responseAck(request, Response.BUSY_HERE); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 点播回复 BUSY_HERE: {}", e.getMessage()); + } + }); + } + + public SIPResponse sendStreamAck(MediaServerItem mediaServerItem, SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) { + + StringBuffer content = new StringBuffer(200); + content.append("v=0\r\n"); + content.append("o=" + sendRtpItem.getChannelId() + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); + content.append("s=Play\r\n"); + content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n"); + content.append("t=0 0\r\n"); + 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"); + if (sendRtpItem.isTcp()) { + content.append("a=connection:new\r\n"); + if (!sendRtpItem.isTcpActive()) { + content.append("a=setup:active\r\n"); + } else { + content.append("a=setup:passive\r\n"); + } + } + content.append("y=" + sendRtpItem.getSsrc() + "\r\n"); + content.append("f=\r\n"); + + try { + return responseSdpAck(request, content.toString(), platform); + } catch (SipException e) { + e.printStackTrace(); + } catch (InvalidArgumentException e) { + e.printStackTrace(); + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } + + public void inviteFromDeviceHandle(SIPRequest request, String requesterId) { + + // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备) + Device device = redisCatchStorage.getDevice(requesterId); + if (device != null) { + logger.info("收到设备" + requesterId + "的语音广播Invite请求"); + try { + responseAck(request, Response.TRYING); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage()); + } + String contentString = new String(request.getRawContent()); + // jainSip不支持y=字段, 移除移除以解析。 + String substring = contentString; + String ssrc = "0000000404"; + int ssrcIndex = contentString.indexOf("y="); + if (ssrcIndex > 0) { + substring = contentString.substring(0, ssrcIndex); + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + } + ssrcIndex = substring.indexOf("f="); + if (ssrcIndex > 0) { + substring = contentString.substring(0, ssrcIndex); + } + SessionDescription sdp = null; + try { + sdp = SdpFactory.getInstance().createSessionDescription(substring); + // 获取支持的格式 + Vector mediaDescriptions = sdp.getMediaDescriptions(true); + // 查看是否支持PS 负载96 + int port = -1; + //boolean recvonly = false; + 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; + } + } + if (port == -1) { + logger.info("不支持的媒体格式,返回415"); + // 回复不支持的格式 + try { + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415 + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 不支持的媒体格式,返回415, {}", e.getMessage()); + } + return; + } + String username = sdp.getOrigin().getUsername(); + String addressStr = sdp.getOrigin().getAddress(); + logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc); + } catch (SdpException e) { + logger.error("[SDP解析异常]", e); + } + + + + } else { + logger.warn("来自无效设备/平台的请求"); + try { + responseAck(request, Response.BAD_REQUEST);; // 不支持的格式,发415 + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] invite 来自无效设备/平台的请求, {}", e.getMessage()); + } + } + } } 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 2e58d9d3..db922f98 100644 --- 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 @@ -1,11 +1,11 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; -import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.conf.UserSetup; +import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; @@ -14,18 +14,22 @@ 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.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.GpsUtil; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import gov.nist.javax.sip.message.SIPRequest; 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.util.StringUtils; +import org.springframework.util.ObjectUtils; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; @@ -34,9 +38,10 @@ import javax.sip.header.FromHeader; import javax.sip.message.Response; import java.text.ParseException; import java.util.Iterator; +import java.util.concurrent.ConcurrentLinkedQueue; /** - * SIP命令类型: NOTIFY请求 + * SIP命令类型: NOTIFY请求,这是作为上级发送订阅请求后,设备才会响应的 */ @Component public class NotifyRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { @@ -45,10 +50,13 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements private final static Logger logger = LoggerFactory.getLogger(NotifyRequestProcessor.class); @Autowired - private UserSetup userSetup; + private UserSetting userSetting; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; + + @Autowired + private EventPublisher eventPublisher; @Autowired private SipConfig sipConfig; @@ -59,17 +67,20 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @Autowired private EventPublisher publisher; - @Autowired - private DeviceOffLineDetector offLineDetector; - - private static final String NOTIFY_CATALOG = "Catalog"; - private static final String NOTIFY_ALARM = "Alarm"; - private static final String NOTIFY_MOBILE_POSITION = "MobilePosition"; - private String method = "NOTIFY"; + private final String method = "NOTIFY"; @Autowired private SIPProcessorObserver sipProcessorObserver; + @Autowired + private IDeviceChannelService deviceChannelService; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + @Override public void afterPropertiesSet() throws Exception { // 添加消息处理的订阅 @@ -79,25 +90,42 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements @Override public void process(RequestEvent evt) { try { - Element rootElement = getRootElement(evt); - String cmd = XmlUtil.getText(rootElement, "CmdType"); - - if (NOTIFY_CATALOG.equals(cmd)) { - logger.info("接收到Catalog通知"); - processNotifyCatalogList(evt); - } else if (NOTIFY_ALARM.equals(cmd)) { - logger.info("接收到Alarm通知"); - processNotifyAlarm(evt); - } else if (NOTIFY_MOBILE_POSITION.equals(cmd)) { - logger.info("接收到MobilePosition通知"); - processNotifyMobilePosition(evt); - } else { - logger.info("接收到消息:" + cmd); - responseAck(evt, Response.OK); - } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null); + }catch (SipException | InvalidArgumentException | ParseException e) { e.printStackTrace(); } + boolean runed = !taskQueue.isEmpty(); + taskQueue.offer(new HandlerCatchData(evt, null, null)); + if (!runed) { + taskExecutor.execute(()-> { + while (!taskQueue.isEmpty()) { + try { + HandlerCatchData take = taskQueue.poll(); + Element rootElement = getRootElement(take.getEvt()); + if (rootElement == null) { + logger.error("处理NOTIFY消息时未获取到消息体,{}", take.getEvt().getRequest()); + continue; + } + String cmd = XmlUtil.getText(rootElement, "CmdType"); + + if (CmdType.CATALOG.equals(cmd)) { + logger.info("接收到Catalog通知"); + processNotifyCatalogList(take.getEvt()); + } else if (CmdType.ALARM.equals(cmd)) { + logger.info("接收到Alarm通知"); + processNotifyAlarm(take.getEvt()); + } else if (CmdType.MOBILE_POSITION.equals(cmd)) { + logger.info("接收到MobilePosition通知"); + processNotifyMobilePosition(take.getEvt()); + } else { + logger.info("接收到消息:" + cmd); + } + } catch (DocumentException e) { + logger.error("处理NOTIFY消息时错误", e); + } + } + }); + } } /** @@ -107,19 +135,30 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements */ private void processNotifyMobilePosition(RequestEvent evt) { try { + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); + // 回复 200 OK Element rootElement = getRootElement(evt); + if (rootElement == null) { + logger.error("处理MobilePosition移动位置Notify时未获取到消息体,{}", evt.getRequest()); + return; + } + MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setCreateTime(DateUtil.getNow()); Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getTextTrim().toString(); + String channelId = deviceIdElement.getTextTrim().toString(); Device device = redisCatchStorage.getDevice(deviceId); if (device != null) { - if (!StringUtils.isEmpty(device.getName())) { + if (!ObjectUtils.isEmpty(device.getName())) { mobilePosition.setDeviceName(device.getName()); } } mobilePosition.setDeviceId(XmlUtil.getText(rootElement, "DeviceID")); - mobilePosition.setTime(XmlUtil.getText(rootElement, "Time")); + mobilePosition.setChannelId(channelId); + String time = XmlUtil.getText(rootElement, "Time"); + mobilePosition.setTime(time); mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude"))); mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude"))); if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) { @@ -137,19 +176,43 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements } else { mobilePosition.setAltitude(0.0); } + logger.info("[收到移动位置订阅通知]:{}/{}->{}.{}", mobilePosition.getDeviceId(), mobilePosition.getChannelId(), + mobilePosition.getLongitude(), mobilePosition.getLatitude()); mobilePosition.setReportSource("Mobile Position"); - BaiduPoint bp = new BaiduPoint(); - bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude())); - logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat()); - mobilePosition.setGeodeticSystem("BD-09"); - mobilePosition.setCnLng(bp.getBdLng()); - mobilePosition.setCnLat(bp.getBdLat()); - if (!userSetup.getSavePositionHistory()) { - storager.clearMobilePositionsByDeviceId(deviceId); + + + // 更新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()); + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); } - storager.insertMobilePosition(mobilePosition); - responseAck(evt, Response.OK); - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + + storager.updateChannelPosition(deviceChannel); + + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", time); + jsonObject.put("serial", deviceId); + jsonObject.put("code", channelId); + 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) { e.printStackTrace(); } } @@ -164,20 +227,37 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements return; } try { + FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); + String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); + Element rootElement = getRootElement(evt); + if (rootElement == null) { + logger.error("处理alarm设备报警Notify时未获取到消息体{}", evt.getRequest()); + return; + } Element deviceIdElement = rootElement.element("DeviceID"); - String deviceId = deviceIdElement.getText().toString(); + String channelId = deviceIdElement.getText().toString(); Device device = redisCatchStorage.getDevice(deviceId); if (device == null) { + logger.warn("[ NotifyAlarm ] 未找到设备:{}", deviceId); return; } rootElement = getRootElement(evt, device.getCharset()); + if (rootElement == null) { + logger.warn("[ NotifyAlarm ] content cannot be null, {}", evt.getRequest()); + return; + } DeviceAlarm deviceAlarm = new DeviceAlarm(); deviceAlarm.setDeviceId(deviceId); deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority")); deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod")); - deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime")); + String alarmTime = XmlUtil.getText(rootElement, "AlarmTime"); + if (alarmTime == null) { + logger.warn("[ NotifyAlarm ] AlarmTime cannot be null"); + return; + } + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime)); if (XmlUtil.getText(rootElement, "AlarmDescription") == null) { deviceAlarm.setAlarmDescription(""); } else { @@ -193,33 +273,56 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements } else { deviceAlarm.setLatitude(0.00); } - - if (deviceAlarm.getAlarmMethod().equals("4")) { + logger.info("[收到Notify-Alarm]:{}/{}", device.getDeviceId(), deviceAlarm.getChannelId()); + if ("4".equals(deviceAlarm.getAlarmMethod())) { MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setCreateTime(DateUtil.getNow()); mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); mobilePosition.setTime(deviceAlarm.getAlarmTime()); mobilePosition.setLongitude(deviceAlarm.getLongitude()); mobilePosition.setLatitude(deviceAlarm.getLatitude()); mobilePosition.setReportSource("GPS Alarm"); - BaiduPoint bp = new BaiduPoint(); - bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude())); - logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat()); - mobilePosition.setGeodeticSystem("BD-09"); - mobilePosition.setCnLng(bp.getBdLng()); - mobilePosition.setCnLat(bp.getBdLat()); - if (!userSetup.getSavePositionHistory()) { - storager.clearMobilePositionsByDeviceId(deviceId); + + // 更新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()); + + deviceChannel = deviceChannelService.updateGps(deviceChannel, device); + + mobilePosition.setLongitudeWgs84(deviceChannel.getLongitudeWgs84()); + mobilePosition.setLatitudeWgs84(deviceChannel.getLatitudeWgs84()); + mobilePosition.setLongitudeGcj02(deviceChannel.getLongitudeGcj02()); + mobilePosition.setLatitudeGcj02(deviceChannel.getLatitudeGcj02()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); } - storager.insertMobilePosition(mobilePosition); + + storager.updateChannelPosition(deviceChannel); + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", mobilePosition.getTime()); + jsonObject.put("serial", deviceChannel.getDeviceId()); + jsonObject.put("code", deviceChannel.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); + } // TODO: 需要实现存储报警信息、报警分类 // 回复200 OK - responseAck(evt, Response.OK); - if (offLineDetector.isOnline(deviceId)) { + if (redisCatchStorage.deviceIsOnline(deviceId)) { publisher.deviceAlarmEventPublish(deviceAlarm); } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + } catch (DocumentException e) { e.printStackTrace(); } } @@ -234,13 +337,15 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); String deviceId = SipUtils.getUserIdFromFromHeader(fromHeader); - Element rootElement = getRootElement(evt); Device device = redisCatchStorage.getDevice(deviceId); - if (device == null) { + if (device == null || device.getOnline() == 0) { + logger.warn("[收到目录订阅]:{}, 但是设备已经离线", (device != null ? device.getDeviceId():"" )); return; } - if (device != null ) { - rootElement = getRootElement(evt, device.getCharset()); + Element rootElement = getRootElement(evt, device.getCharset()); + if (rootElement == null) { + logger.warn("[ 收到目录订阅 ] content cannot be null, {}", evt.getRequest()); + return; } Element deviceListElement = rootElement.element("DeviceList"); if (deviceListElement == null) { @@ -257,158 +362,68 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements continue; } Element eventElement = itemDevice.element("Event"); - DeviceChannel channel = channelContentHander(itemDevice); - switch (eventElement.getText().toUpperCase()) { - case "ON" : // 上线 - logger.info("收到来自设备【{}】的通道【{}】上线通知", device.getDeviceId(), channel.getChannelId()); + String event; + if (eventElement == null) { + logger.warn("[收到目录订阅]:{}, 但是Event为空, 设为默认值 ADD", (device != null ? device.getDeviceId():"" )); + event = CatalogEvent.ADD; + }else { + event = eventElement.getText().toUpperCase(); + } + DeviceChannel channel = XmlUtil.channelContentHander(itemDevice, device, event); + channel.setDeviceId(device.getDeviceId()); + logger.info("[收到目录订阅]:{}/{}", device.getDeviceId(), channel.getChannelId()); + switch (event) { + case CatalogEvent.ON: + // 上线 + logger.info("[收到通道上线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); storager.deviceChannelOnline(deviceId, channel.getChannelId()); - // 回复200 OK - responseAck(evt, Response.OK); break; - case "OFF" : // 离线 - logger.info("收到来自设备【{}】的通道【{}】离线通知", device.getDeviceId(), channel.getChannelId()); + case CatalogEvent.OFF : + // 离线 + logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); storager.deviceChannelOffline(deviceId, channel.getChannelId()); - // 回复200 OK - responseAck(evt, Response.OK); break; - case "VLOST" : // 视频丢失 - logger.info("收到来自设备【{}】的通道【{}】视频丢失通知", device.getDeviceId(), channel.getChannelId()); + case CatalogEvent.VLOST: + // 视频丢失 + logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); storager.deviceChannelOffline(deviceId, channel.getChannelId()); - // 回复200 OK - responseAck(evt, Response.OK); break; - case "DEFECT" : // 故障 - // 回复200 OK - responseAck(evt, Response.OK); + case CatalogEvent.DEFECT: + // 故障 break; - case "ADD" : // 增加 - logger.info("收到来自设备【{}】的增加通道【{}】通知", device.getDeviceId(), channel.getChannelId()); - storager.updateChannel(deviceId, channel); - responseAck(evt, Response.OK); + case CatalogEvent.ADD: + // 增加 + logger.info("[收到增加通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deviceChannelService.updateChannel(deviceId, channel); break; - case "DEL" : // 删除 - logger.info("收到来自设备【{}】的删除通道【{}】通知", device.getDeviceId(), channel.getChannelId()); + case CatalogEvent.DEL: + // 删除 + logger.info("[收到删除通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); storager.delChannel(deviceId, channel.getChannelId()); - responseAck(evt, Response.OK); break; - case "UPDATE" : // 更新 - logger.info("收到来自设备【{}】的更新通道【{}】通知", device.getDeviceId(), channel.getChannelId()); - storager.updateChannel(deviceId, channel); - responseAck(evt, Response.OK); + case CatalogEvent.UPDATE: + // 更新 + logger.info("[收到更新通道通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId()); + deviceChannelService.updateChannel(deviceId, channel); break; default: - responseAck(evt, Response.BAD_REQUEST, "event not found"); + logger.warn("[ NotifyCatalog ] event not found : {}", event ); } + // 转发变化信息 + eventPublisher.catalogEventPublish(null, channel, event); } - - // RequestMessage msg = new RequestMessage(); - // msg.setDeviceId(deviceId); - // msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG); - // msg.setData(device); - // deferredResultHolder.invokeResult(msg); - - if (offLineDetector.isOnline(deviceId)) { - publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE); - } } - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + } catch (DocumentException e) { e.printStackTrace(); } } - public DeviceChannel channelContentHander(Element itemDevice){ - Element channdelNameElement = itemDevice.element("Name"); - String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : ""; - Element statusElement = itemDevice.element("Status"); - String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON"; - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setName(channelName); - Element channdelIdElement = itemDevice.element("DeviceID"); - String channelId = channdelIdElement != null ? channdelIdElement.getTextTrim().toString() : ""; - deviceChannel.setChannelId(channelId); - // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 - if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) { - deviceChannel.setStatus(1); - } - if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { - deviceChannel.setStatus(0); - } - - deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer")); - deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model")); - deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner")); - deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode")); - deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block")); - deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address")); - if (XmlUtil.getText(itemDevice, "Parental") == null - || XmlUtil.getText(itemDevice, "Parental") == "") { - deviceChannel.setParental(0); - } else { - deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental"))); - } - deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID")); - if (XmlUtil.getText(itemDevice, "SafetyWay") == null - || XmlUtil.getText(itemDevice, "SafetyWay") == "") { - deviceChannel.setSafetyWay(0); - } else { - deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay"))); - } - if (XmlUtil.getText(itemDevice, "RegisterWay") == null - || XmlUtil.getText(itemDevice, "RegisterWay") == "") { - deviceChannel.setRegisterWay(1); - } else { - deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay"))); - } - deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum")); - if (XmlUtil.getText(itemDevice, "Certifiable") == null - || XmlUtil.getText(itemDevice, "Certifiable") == "") { - deviceChannel.setCertifiable(0); - } else { - deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable"))); - } - if (XmlUtil.getText(itemDevice, "ErrCode") == null - || XmlUtil.getText(itemDevice, "ErrCode") == "") { - deviceChannel.setErrCode(0); - } else { - deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode"))); - } - deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime")); - deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy")); - deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress")); - if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") == "") { - deviceChannel.setPort(0); - } else { - deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port"))); - } - deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password")); - if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) { - deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude"))); - } else { - deviceChannel.setLongitude(0.00); - } - if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) { - deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude"))); - } else { - deviceChannel.setLatitude(0.00); - } - if (XmlUtil.getText(itemDevice, "PTZType") == null - || XmlUtil.getText(itemDevice, "PTZType") == "") { - deviceChannel.setPTZType(0); - } else { - deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType"))); - } - deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC - return deviceChannel; - } - - - public void setCmder(SIPCommander cmder) { } - public void setStorager(IVideoManagerStorager storager) { + public void setStorager(IVideoManagerStorage storager) { this.storager = storager; } @@ -422,10 +437,6 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) { } - public void setOffLineDetector(DeviceOffLineDetector offLineDetector) { - this.offLineDetector = offLineDetector; - } - public IRedisCatchStorage getRedisCatchStorage() { return redisCatchStorage; } 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 053bf9a9..0f37bde6 100644 --- 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 @@ -1,35 +1,36 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; -import com.genersoft.iot.vmp.common.VideoManagerConstants; 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.auth.RegisterLogicHandler; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate; -import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 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.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.utils.DateUtil; import gov.nist.javax.sip.RequestEventExt; import gov.nist.javax.sip.address.AddressImpl; import gov.nist.javax.sip.address.SipUri; -import gov.nist.javax.sip.header.Expires; import gov.nist.javax.sip.header.SIPDateHeader; +import gov.nist.javax.sip.message.SIPRequest; 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 org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; -import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; -import javax.sip.ServerTransaction; import javax.sip.SipException; -import javax.sip.header.*; -import javax.sip.message.Request; +import javax.sip.header.AuthorizationHeader; +import javax.sip.header.ContactHeader; +import javax.sip.header.FromHeader; +import javax.sip.header.ViaHeader; import javax.sip.message.Response; import java.security.NoSuchAlgorithmException; import java.text.ParseException; @@ -42,160 +43,136 @@ import java.util.Locale; @Component public class RegisterRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class); + private final Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class); - public String method = "REGISTER"; + public final String method = "REGISTER"; - @Autowired - private SipConfig sipConfig; + @Autowired + private SipConfig sipConfig; - @Autowired - private RegisterLogicHandler handler; + @Autowired + private SIPProcessorObserver sipProcessorObserver; - @Autowired - private IRedisCatchStorage redisCatchStorage; + @Autowired + private IDeviceService deviceService; - @Autowired - private IVideoManagerStorager storager; + @Autowired + private SIPSender sipSender; - @Autowired - private EventPublisher publisher; + @Autowired + private UserSetting userSetting; - @Autowired - private SIPProcessorObserver sipProcessorObserver; + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } - @Override - public void afterPropertiesSet() throws Exception { - // 添加消息处理的订阅 - sipProcessorObserver.addRequestProcessor(method, this); - } + /** + * 收到注册请求 处理 + * + * @param evt + */ + @Override + public void process(RequestEvent evt) { + try { + RequestEventExt evtExt = (RequestEventExt) evt; + String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort(); + logger.info("[注册请求] 开始处理: {}", requestAddress); + SIPRequest request = (SIPRequest)evt.getRequest(); + Response response = null; + boolean passwordCorrect = false; + // 注册标志 + boolean registerFlag; + FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME); + AddressImpl address = (AddressImpl) fromHeader.getAddress(); + SipUri uri = (SipUri) address.getURI(); + String deviceId = uri.getUser(); + Device device = deviceService.getDevice(deviceId); + 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("[注册请求] 回复401: {}", requestAddress); + response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); + new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain()); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + return; + } - /** - * 收到注册请求 处理 - * @param evt - */ - @Override - public void process(RequestEvent evt) { - try { - RequestEventExt evtExt = (RequestEventExt)evt; - String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort(); - logger.info("[{}] 收到注册请求,开始处理", requestAddress); - Request request = evt.getRequest(); + // 校验密码是否正确 + passwordCorrect = ObjectUtils.isEmpty(password) || + new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, password); - Response response = null; - boolean passwordCorrect = false; - // 注册标志 0:未携带授权头或者密码错误 1:注册成功 2:注销成功 - int registerFlag = 0; - FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME); - AddressImpl address = (AddressImpl) fromHeader.getAddress(); - SipUri uri = (SipUri) address.getURI(); - String deviceId = uri.getUser(); - Device device = redisCatchStorage.getDevice(deviceId); - AuthorizationHeader authorhead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); - // 校验密码是否正确 - if (authorhead != null) { - passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, - sipConfig.getPassword()); - } - if (StringUtils.isEmpty(sipConfig.getPassword())){ - passwordCorrect = true; - } + if (!passwordCorrect) { + // 注册失败 + response = getMessageFactory().createResponse(Response.FORBIDDEN, request); + response.setReasonPhrase("wrong password"); + logger.info("[注册请求] 密码/SIP服务器ID错误, 回复403: {}", requestAddress); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + return; + } - // 未携带授权头或者密码错误 回复401 - if (authorhead == null ) { + // 携带授权头并且密码正确 + response = getMessageFactory().createResponse(Response.OK, request); + // 添加date头 + SIPDateHeader dateHeader = new SIPDateHeader(); + // 使用自己修改的 + WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis()); + dateHeader.setDate(wvpSipDate); + response.addHeader(dateHeader); - logger.info("[{}] 未携带授权头 回复401", requestAddress); - response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); - new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain()); - }else { - if (!passwordCorrect){ - // 注册失败 - response = getMessageFactory().createResponse(Response.FORBIDDEN, request); - response.setReasonPhrase("wrong password"); - logger.info("[{}] 密码/SIP服务器ID错误, 回复403", requestAddress); - }else { - // 携带授权头并且密码正确 - response = getMessageFactory().createResponse(Response.OK, request); - // 添加date头 - SIPDateHeader dateHeader = new SIPDateHeader(); - // 使用自己修改的 - WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis()); - dateHeader.setDate(wvpSipDate); - response.addHeader(dateHeader); + if (request.getExpires() == null) { + response = getMessageFactory().createResponse(Response.BAD_REQUEST, request); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + return; + } + // 添加Contact头 + response.addHeader(request.getHeader(ContactHeader.NAME)); + // 添加Expires头 + response.addHeader(request.getExpires()); - ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME); - if (expiresHeader == null) { - response = getMessageFactory().createResponse(Response.BAD_REQUEST, request); - ServerTransaction serverTransaction = getServerTransaction(evt); - serverTransaction.sendResponse(response); - if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete(); - return; - } - // 添加Contact头 - response.addHeader(request.getHeader(ContactHeader.NAME)); - // 添加Expires头 - response.addHeader(request.getExpires()); + RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, + userSetting.getSipUseSourceIpAsRemoteAddress()); - // 获取到通信地址等信息 - ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); - String received = viaHeader.getReceived(); - int rPort = viaHeader.getRPort(); - // 解析本地地址替代 - if (StringUtils.isEmpty(received) || rPort == -1) { - received = viaHeader.getHost(); - rPort = viaHeader.getPort(); - } - // - - if (device == null) { - device = new Device(); - device.setStreamMode("UDP"); - device.setCharset("gb2312"); - device.setDeviceId(deviceId); - device.setFirsRegister(true); - } - device.setIp(received); - device.setPort(rPort); - device.setHostAddress(received.concat(":").concat(String.valueOf(rPort))); - // 注销成功 - if (expiresHeader.getExpires() == 0) { - registerFlag = 2; - } - // 注册成功 - else { - device.setExpires(expiresHeader.getExpires()); - registerFlag = 1; - // 判断TCP还是UDP - boolean isTcp = false; - ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); - String transport = reqViaHeader.getTransport(); - if (transport.equals("TCP")) { - isTcp = true; - } - device.setTransport(isTcp ? "TCP" : "UDP"); - } - } - } - - ServerTransaction serverTransaction = getServerTransaction(evt); - serverTransaction.sendResponse(response); - if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete(); - // 注册成功 - // 保存到redis - // 下发catelog查询目录 - if (registerFlag == 1 ) { - logger.info("[{}] 注册成功! deviceId:" + device.getDeviceId(), requestAddress); - publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_REGISTER); - // 重新注册更新设备和通道,以免设备替换或更新后信息无法更新 - handler.onRegister(device); - } else if (registerFlag == 2) { - logger.info("[{}] 注销成功! deviceId:" + device.getDeviceId(), requestAddress); - publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER); - } - } catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) { - e.printStackTrace(); - } - - } + if (device == null) { + device = new Device(); + device.setStreamMode("UDP"); + device.setCharset("GB2312"); + device.setGeoCoordSys("WGS84"); + device.setTreeType("CivilCode"); + device.setDeviceId(deviceId); + device.setOnline(0); + } + device.setIp(remoteAddressInfo.getIp()); + device.setPort(remoteAddressInfo.getPort()); + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); + device.setLocalIp(request.getLocalAddress().getHostAddress()); + if (request.getExpires().getExpires() == 0) { + // 注销成功 + registerFlag = false; + } else { + // 注册成功 + device.setExpires(request.getExpires().getExpires()); + registerFlag = true; + // 判断TCP还是UDP + ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME); + String transport = reqViaHeader.getTransport(); + device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP"); + } + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + // 注册成功 + // 保存到redis + if (registerFlag) { + logger.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress); + device.setRegisterTime(DateUtil.getNow()); + deviceService.online(device); + } else { + logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress); + deviceService.offline(deviceId); + } + } catch (SipException | NoSuchAlgorithmException | ParseException e) { + e.printStackTrace(); + } + } } 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 be4b2ce9..d32d194f 100644 --- 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,35 +1,61 @@ 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.SubscribeHolder; +import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo; +import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeHandlerTask; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; +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.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.gb28181.utils.XmlUtil; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.SipProviderImpl; +import gov.nist.javax.sip.message.SIPRequest; +import gov.nist.javax.sip.message.SIPResponse; +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; -import javax.sip.InvalidArgumentException; -import javax.sip.RequestEvent; -import javax.sip.ServerTransaction; -import javax.sip.SipException; +import javax.sip.*; import javax.sip.header.ExpiresHeader; -import javax.sip.message.Request; import javax.sip.message.Response; import java.text.ParseException; /** * SIP命令类型: SUBSCRIBE请求 + * @author lin */ @Component public class SubscribeRequestProcessor extends SIPRequestProcessorParent implements InitializingBean, ISIPRequestProcessor { - private Logger logger = LoggerFactory.getLogger(SubscribeRequestProcessor.class); - private String method = "SUBSCRIBE"; + 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; + + @Autowired + private SIPSender sipSender; + @Override public void afterPropertiesSet() throws Exception { // 添加消息处理的订阅 @@ -39,37 +65,137 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme /** * 处理SUBSCRIBE请求 * - * @param evt + * @param evt 事件 */ @Override public void process(RequestEvent evt) { - Request request = evt.getRequest(); - + SIPRequest request = (SIPRequest) evt.getRequest(); try { - Response response = null; - response = getMessageFactory().createResponse(200, request); - if (response != null) { - ExpiresHeader expireHeader = getHeaderFactory().createExpiresHeader(30); - response.setExpires(expireHeader); + Element rootElement = getRootElement(evt); + if (rootElement == null) { + logger.error("处理SUBSCRIBE请求 未获取到消息体{}", evt.getRequest()); + return; } - logger.info("response : " + response.toString()); - ServerTransaction transaction = getServerTransaction(evt); - if (transaction != null) { - transaction.sendResponse(response); - transaction.getDialog().delete(); - transaction.terminate(); + String cmd = XmlUtil.getText(rootElement, "CmdType"); + if (CmdType.MOBILE_POSITION.equals(cmd)) { + processNotifyMobilePosition(request, rootElement); +// } else if (CmdType.ALARM.equals(cmd)) { +// logger.info("接收到Alarm订阅"); +// processNotifyAlarm(serverTransaction, rootElement); + } else if (CmdType.CATALOG.equals(cmd)) { + processNotifyCatalogList(request, rootElement); } else { - logger.info("processRequest serverTransactionId is null."); - } + logger.info("接收到消息:" + cmd); - } catch (ParseException e) { - e.printStackTrace(); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { + Response response = getMessageFactory().createResponse(200, request); + if (response != null) { + ExpiresHeader expireHeader = getHeaderFactory().createExpiresHeader(30); + response.setExpires(expireHeader); + } + logger.info("response : " + response); + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); + } + } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) { e.printStackTrace(); } - + } + /** + * 处理移动位置订阅消息 + */ + private void processNotifyMobilePosition(SIPRequest request, Element rootElement) throws SipException { + if (request == null) { + return; + } + String platformId = SipUtils.getUserIdFromFromHeader(request); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId); + if (platform == null) { + return; + } + + String sn = XmlUtil.getText(rootElement, "SN"); + logger.info("[回复上级的移动位置订阅请求]: {}", platformId); + StringBuilder resultXml = new StringBuilder(200); + resultXml.append("\r\n") + .append("\r\n") + .append("MobilePosition\r\n") + .append("").append(sn).append("\r\n") + .append("").append(deviceId).append("\r\n") + .append("OK\r\n") + .append("\r\n"); + + if (subscribeInfo.getExpires() > 0) { + // GPS上报时间间隔 + String interval = XmlUtil.getText(rootElement, "Interval"); + if (interval == null) { + subscribeInfo.setGpsInterval(5); + }else { + subscribeInfo.setGpsInterval(Integer.parseInt(interval)); + } + subscribeInfo.setSn(sn); + } + + try { + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId); + SIPResponse response = responseXmlAck(request, resultXml.toString(), parentPlatform, subscribeInfo.getExpires()); + if (subscribeInfo.getExpires() == 0) { + subscribeHolder.removeMobilePositionSubscribe(platformId); + }else { + subscribeInfo.setResponse(response); + subscribeHolder.putMobilePositionSubscribe(platformId, subscribeInfo); + } + + } catch (SipException | InvalidArgumentException | ParseException e) { + e.printStackTrace(); + } + } + + private void processNotifyAlarm(RequestEvent evt, Element rootElement) { + + } + + private void processNotifyCatalogList(SIPRequest request, Element rootElement) throws SipException { + if (request == null) { + return; + } + String platformId = SipUtils.getUserIdFromFromHeader(request); + String deviceId = XmlUtil.getText(rootElement, "DeviceID"); + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + if (platform == null){ + return; + } + SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId); + + String sn = XmlUtil.getText(rootElement, "SN"); + logger.info("[回复上级的目录订阅请求]: {}/{}", platformId, deviceId); + StringBuilder resultXml = new StringBuilder(200); + resultXml.append("\r\n") + .append("\r\n") + .append("Catalog\r\n") + .append("").append(sn).append("\r\n") + .append("").append(deviceId).append("\r\n") + .append("OK\r\n") + .append("\r\n"); + + if (subscribeInfo.getExpires() > 0) { + subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo); + }else if (subscribeInfo.getExpires() == 0) { + subscribeHolder.removeCatalogSubscribe(platformId); + } + try { + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId); + SIPResponse response = responseXmlAck(request, resultXml.toString(), parentPlatform, subscribeInfo.getExpires()); + if (subscribeInfo.getExpires() == 0) { + subscribeHolder.removeCatalogSubscribe(platformId); + }else { + subscribeInfo.setResponse(response); + subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo); + } + } catch (SipException | InvalidArgumentException | ParseException e) { + e.printStackTrace(); + } + } } 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 new file mode 100644 index 00000000..a4d49d5a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java @@ -0,0 +1,139 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.info; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.transmit.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.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 org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.header.*; +import javax.sip.message.Response; +import java.text.ParseException; + +@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; + + @Autowired + private SipSubscribe sipSubscribe; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + @Autowired + private SIPCommander cmder; + + @Autowired + private VideoStreamSessionManager sessionManager; + + @Override + public void afterPropertiesSet() throws Exception { + // 添加消息处理的订阅 + sipProcessorObserver.addRequestProcessor(method, this); + } + + @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) { + // 不存在则回复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.getStreamId(); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); + if (null == streamInfo) { + responseAck(request, Response.NOT_FOUND, "stream " + streamId + " not found"); + return; + } + Device device1 = storager.queryVideoDevice(streamInfo.getDeviceID()); + cmder.playbackControlCmd(device1,streamInfo,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()); + } + }); + } + } + } catch (SipException e) { + logger.warn("SIP 回复错误", e); + } catch (InvalidArgumentException e) { + logger.warn("参数无效", e); + } catch (ParseException e) { + logger.warn("SIP回复时解析异常", e); + } + } + + +} 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 0aab8716..527b5d09 100644 --- 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 @@ -1,13 +1,18 @@ 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.SsrcTransaction; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 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.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.DocumentException; import org.dom4j.Element; import org.slf4j.Logger; @@ -19,6 +24,7 @@ import org.springframework.stereotype.Component; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; import javax.sip.SipException; +import javax.sip.header.CallIdHeader; import javax.sip.message.Response; import java.text.ParseException; import java.util.Map; @@ -37,11 +43,17 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement private SIPProcessorObserver sipProcessorObserver; @Autowired - private IVideoManagerStorager storage; + private IVideoManagerStorage storage; + + @Autowired + private SipSubscribe sipSubscribe; @Autowired private IRedisCatchStorage redisCatchStorage; + @Autowired + private VideoStreamSessionManager sessionManager; + @Override public void afterPropertiesSet() throws Exception { // 添加消息处理的订阅 @@ -54,18 +66,56 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement @Override public void process(RequestEvent evt) { + SIPRequest sipRequest = (SIPRequest)evt.getRequest(); + logger.info("接收到消息:" + evt.getRequest()); logger.debug("接收到消息:" + evt.getRequest()); String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest()); + CallIdHeader callIdHeader = sipRequest.getCallIdHeader(); + // 先从会话内查找 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); + // 兼容海康 媒体通知 消息from字段不是设备ID的问题 + if (ssrcTransaction != null) { + deviceId = ssrcTransaction.getDeviceId(); + } + SIPRequest request = (SIPRequest) evt.getRequest(); // 查询设备是否存在 Device device = redisCatchStorage.getDevice(deviceId); // 查询上级平台是否存在 ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId); try { + if (device != null && parentPlatform != null) { + 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) { // 不存在则回复404 - responseAck(evt, Response.NOT_FOUND, "device id not found"); + responseAck(request, Response.NOT_FOUND, "device "+ deviceId +" not found"); + logger.warn("[设备未找到 ]deviceId: {}, callId: {}", deviceId, callIdHeader.getCallId()); + 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 { - Element rootElement = getRootElement(evt); + Element rootElement = null; + try { + rootElement = getRootElement(evt); + if (rootElement == null) { + logger.error("处理MESSAGE请求 未获取到消息体{}", evt.getRequest()); + responseAck(request, Response.BAD_REQUEST, "content is null"); + return; + } + } catch (DocumentException e) { + logger.warn("解析XML消息内容异常", e); + // 不存在则回复404 + responseAck(request, Response.BAD_REQUEST, e.getMessage()); + } String name = rootElement.getName(); IMessageHandler messageHandler = messageHandlerMap.get(name); if (messageHandler != null) { @@ -77,7 +127,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement }else { // 不支持的message // 不存在则回复415 - responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response"); + responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response"); } } } catch (SipException e) { @@ -86,8 +136,6 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement logger.warn("参数无效", e); } catch (ParseException e) { logger.warn("SIP回复时解析异常", e); - } catch (DocumentException e) { - logger.warn("解析XML消息内容异常", 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 980ec5d7..f97a6592 100644 --- 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 @@ -8,25 +8,25 @@ 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.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.SpringBeanFactory; -import gov.nist.javax.sip.SipStackImpl; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; 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 org.springframework.util.StringUtils; -import javax.sip.ListeningPoint; -import javax.sip.ObjectInUseException; -import javax.sip.RequestEvent; -import javax.sip.SipProvider; +import javax.sip.*; import javax.sip.address.SipURI; import javax.sip.header.HeaderAddress; import javax.sip.header.ToHeader; +import javax.sip.message.Response; +import java.text.ParseException; import java.util.Iterator; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; @@ -41,7 +41,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent private ControlMessageHandler controlMessageHandler; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private SIPCommander cmder; @@ -49,6 +49,10 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent @Autowired private SIPCommanderFroPlatform cmderFroPlatform; + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + @Override public void afterPropertiesSet() throws Exception { controlMessageHandler.addHandler(cmdType, this); @@ -62,51 +66,76 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent @Override public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + SIPRequest request = (SIPRequest) evt.getRequest(); + // 此处是上级发出的DeviceControl指令 - String targetGBId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser(); + String targetGBId = ((SipURI) request.getToHeader().getAddress().getURI()).getUser(); String channelId = getText(rootElement, "DeviceID"); // 远程启动功能 - if (!StringUtils.isEmpty(getText(rootElement, "TeleBoot"))) { + if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) { if (parentPlatform.getServerGBId().equals(targetGBId)) { // 远程启动本平台:需要在重新启动程序后先对SipStack解绑 logger.info("执行远程启动本平台命令"); - cmderFroPlatform.unregister(parentPlatform, null, null); - - Thread restartThread = new Thread(new Runnable() { - @Override - public void run() { - 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 ignored) { - } catch (ObjectInUseException e) { - e.printStackTrace(); - } - } + 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()); +// } }); - - restartThread.setDaemon(false); - restartThread.start(); } else { // 远程启动指定设备 } } // 云台/前端控制命令 - if (!StringUtils.isEmpty(getText(rootElement,"PTZCmd")) && !parentPlatform.getServerGBId().equals(targetGBId)) { + if (!ObjectUtils.isEmpty(getText(rootElement,"PTZCmd")) && !parentPlatform.getServerGBId().equals(targetGBId)) { String cmdString = getText(rootElement,"PTZCmd"); Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); - cmder.fronEndCmd(deviceForPlatform, channelId, cmdString); + if (deviceForPlatform == null) { + try { + responseAck(request, Response.NOT_FOUND); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 错误信息: {}", e.getMessage()); + } + return; + } + try { + cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, 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()); + } + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 云台/前端: {}", e.getMessage()); + } } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java index c546057e..bb34189e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/NotifyMessageHandler.java @@ -7,8 +7,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** - * 命令类型: 通知命令 + * 命令类型: 通知命令, 参看 A.2.5 通知命令 * 命令类型: 状态信息(心跳)报送, 报警通知, 媒体通知, 移动设备位置数据,语音广播通知(TODO), 设备预置位(TODO) + * @author lin */ @Component public class NotifyMessageHandler extends MessageHandlerAbstract implements InitializingBean { 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 a46b0034..09a5ffc2 100644 --- 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 @@ -1,33 +1,48 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.conf.UserSetup; +import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; 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.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.GpsUtil; +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 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.StringUtils; +import org.springframework.util.ObjectUtils; +import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.concurrent.ConcurrentLinkedQueue; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +/** + * 报警事件的处理,参考:9.4 + */ @Component public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class); + private final Logger logger = LoggerFactory.getLogger(AlarmNotifyMessageHandler.class); private final String cmdType = "Alarm"; @Autowired @@ -37,19 +52,29 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme private EventPublisher publisher; @Autowired - private UserSetup userSetup; + private UserSetting userSetting; @Autowired private SipConfig sipConfig; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; @Autowired private IDeviceAlarmService deviceAlarmService; @Autowired - private DeviceOffLineDetector offLineDetector; + private IDeviceChannelService deviceChannelService; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + @Override public void afterPropertiesSet() throws Exception { @@ -58,64 +83,189 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { - if (!sipConfig.isAlarm()) { - return; - } - Element deviceIdElement = rootElement.element("DeviceID"); - String channelId = deviceIdElement.getText().toString(); - DeviceAlarm deviceAlarm = new DeviceAlarm(); - deviceAlarm.setDeviceId(device.getDeviceId()); - deviceAlarm.setChannelId(channelId); - deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority")); - deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod")); - deviceAlarm.setAlarmTime(getText(rootElement, "AlarmTime")); - if (getText(rootElement, "AlarmDescription") == null) { - deviceAlarm.setAlarmDescription(""); - } else { - deviceAlarm.setAlarmDescription(getText(rootElement, "AlarmDescription")); - } - if (NumericUtil.isDouble(getText(rootElement, "Longitude"))) { - deviceAlarm.setLongitude(Double.parseDouble(getText(rootElement, "Longitude"))); - } else { - deviceAlarm.setLongitude(0.00); - } - if (NumericUtil.isDouble(getText(rootElement, "Latitude"))) { - deviceAlarm.setLatitude(Double.parseDouble(getText(rootElement, "Latitude"))); - } else { - deviceAlarm.setLatitude(0.00); + 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()); } + if (isEmpty) { + taskExecutor.execute(() -> { + logger.info("[处理报警通知]待处理数量:{}", taskQueue.size() ); + while (!taskQueue.isEmpty()) { + try { + SipMsgInfo sipMsgInfo = taskQueue.poll(); - if (!StringUtils.isEmpty(deviceAlarm.getAlarmMethod())) { - if ( deviceAlarm.getAlarmMethod().equals("4")) { - MobilePosition mobilePosition = new MobilePosition(); - mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); - mobilePosition.setTime(deviceAlarm.getAlarmTime()); - mobilePosition.setLongitude(deviceAlarm.getLongitude()); - mobilePosition.setLatitude(deviceAlarm.getLatitude()); - mobilePosition.setReportSource("GPS Alarm"); - BaiduPoint bp = new BaiduPoint(); - bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude())); - logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat()); - mobilePosition.setGeodeticSystem("BD-09"); - mobilePosition.setCnLng(bp.getBdLng()); - mobilePosition.setCnLat(bp.getBdLat()); - if (!userSetup.getSavePositionHistory()) { - storager.clearMobilePositionsByDeviceId(device.getDeviceId()); + Element deviceIdElement = sipMsgInfo.getRootElement().element("DeviceID"); + String channelId = deviceIdElement.getText().toString(); + + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setCreateTime(DateUtil.getNow()); + deviceAlarm.setDeviceId(sipMsgInfo.getDevice().getDeviceId()); + deviceAlarm.setChannelId(channelId); + deviceAlarm.setAlarmPriority(getText(sipMsgInfo.getRootElement(), "AlarmPriority")); + deviceAlarm.setAlarmMethod(getText(sipMsgInfo.getRootElement(), "AlarmMethod")); + String alarmTime = XmlUtil.getText(sipMsgInfo.getRootElement(), "AlarmTime"); + if (alarmTime == null) { + continue; + } + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime)); + String alarmDescription = getText(sipMsgInfo.getRootElement(), "AlarmDescription"); + if (alarmDescription == null) { + deviceAlarm.setAlarmDescription(""); + } else { + deviceAlarm.setAlarmDescription(alarmDescription); + } + String longitude = getText(sipMsgInfo.getRootElement(), "Longitude"); + if (longitude != null && NumericUtil.isDouble(longitude)) { + deviceAlarm.setLongitude(Double.parseDouble(longitude)); + } else { + deviceAlarm.setLongitude(0.00); + } + String latitude = getText(sipMsgInfo.getRootElement(), "Latitude"); + if (latitude != null && NumericUtil.isDouble(latitude)) { + deviceAlarm.setLatitude(Double.parseDouble(latitude)); + } else { + deviceAlarm.setLatitude(0.00); + } + + if (!ObjectUtils.isEmpty(deviceAlarm.getAlarmMethod())) { + if ( deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.GPS.getVal() + "")) { + MobilePosition mobilePosition = new MobilePosition(); + 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(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()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); + } + storager.updateChannelPosition(deviceChannel); + + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", mobilePosition.getTime()); + jsonObject.put("serial", deviceChannel.getDeviceId()); + jsonObject.put("code", deviceChannel.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 (!ObjectUtils.isEmpty(deviceAlarm.getDeviceId())) { + if (deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.Video.getVal() + "")) { + deviceAlarm.setAlarmType(getText(sipMsgInfo.getRootElement().element("Info"), "AlarmType")); + } + } + logger.info("[收到报警通知]内容:{}", JSON.toJSONString(deviceAlarm)); + if ("7".equals(deviceAlarm.getAlarmMethod()) ) { + // 发送给平台的报警信息。 发送redis通知 + AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage(); + alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); + alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); + alarmChannelMessage.setGbId(channelId); + redisCatchStorage.sendAlarmMsg(alarmChannelMessage); + continue; + } + + logger.debug("存储报警信息、报警分类"); + // 存储报警信息、报警分类 + if (sipConfig.isAlarm()) { + deviceAlarmService.add(deviceAlarm); + } + + if (redisCatchStorage.deviceIsOnline(sipMsgInfo.getDevice().getDeviceId())) { + publisher.deviceAlarmEventPublish(deviceAlarm); + } + }catch (Exception e) { + logger.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); + } } - storager.insertMobilePosition(mobilePosition); - } - } - logger.debug("存储报警信息、报警分类"); - // 存储报警信息、报警分类 - deviceAlarmService.add(deviceAlarm); - - if (offLineDetector.isOnline(device.getDeviceId())) { - publisher.deviceAlarmEventPublish(deviceAlarm); + }); } } @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + logger.info("收到来自平台[{}]的报警通知", parentPlatform.getServerGBId()); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 报警通知回复: {}", e.getMessage()); + } + Element deviceIdElement = rootElement.element("DeviceID"); + String channelId = deviceIdElement.getText().toString(); + + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setCreateTime(DateUtil.getNow()); + deviceAlarm.setDeviceId(parentPlatform.getServerGBId()); + deviceAlarm.setChannelId(channelId); + deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority")); + deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod")); + String alarmTime = XmlUtil.getText(rootElement, "AlarmTime"); + if (alarmTime == null) { + return; + } + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime)); + String alarmDescription = getText(rootElement, "AlarmDescription"); + if (alarmDescription == null) { + deviceAlarm.setAlarmDescription(""); + } else { + deviceAlarm.setAlarmDescription(alarmDescription); + } + String longitude = getText(rootElement, "Longitude"); + if (longitude != null && NumericUtil.isDouble(longitude)) { + deviceAlarm.setLongitude(Double.parseDouble(longitude)); + } else { + deviceAlarm.setLongitude(0.00); + } + String latitude = getText(rootElement, "Latitude"); + if (latitude != null && NumericUtil.isDouble(latitude)) { + deviceAlarm.setLatitude(Double.parseDouble(latitude)); + } else { + deviceAlarm.setLatitude(0.00); + } + + if (!ObjectUtils.isEmpty(deviceAlarm.getAlarmMethod())) { + + if (deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.Video.getVal() + "")) { + deviceAlarm.setAlarmType(getText(rootElement.element("Info"), "AlarmType")); + } + } + + if (channelId.equals(parentPlatform.getDeviceGBId())) { + // 发送给平台的报警信息。 发送redis通知 + AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage(); + alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); + alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); + alarmChannelMessage.setGbId(channelId); + redisCatchStorage.sendAlarmMsg(alarmChannelMessage); + return; + } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java deleted file mode 100644 index c6c1ab9c..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/CatalogNotifyMessageHandler.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd; - -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.gb28181.bean.Device; -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.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; -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.notify.NotifyMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -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.InvalidArgumentException; -import javax.sip.RequestEvent; -import javax.sip.SipException; -import javax.sip.header.FromHeader; -import javax.sip.message.Response; -import java.text.ParseException; -import java.util.List; - -@Component -public class CatalogNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - - private Logger logger = LoggerFactory.getLogger(CatalogNotifyMessageHandler.class); - private final String cmdType = "Catalog"; - - @Autowired - private NotifyMessageHandler notifyMessageHandler; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private SIPCommanderFroPlatform cmderFroPlatform; - - @Autowired - private SipConfig config; - - @Override - public void afterPropertiesSet() throws Exception { - notifyMessageHandler.addHandler(cmdType, this); - } - - @Override - public void handForDevice(RequestEvent evt, Device device, Element element) { - - } - - @Override - public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { - - String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + parentPlatform.getServerGBId(); - FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); - try { - // 回复200 OK - responseAck(evt, Response.OK); - Element snElement = rootElement.element("SN"); - String sn = snElement.getText(); - // 准备回复通道信息 - List channelReduces = storager.queryChannelListInParentPlatform(parentPlatform.getServerGBId()); - // 查询关联的直播通道 - List gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId()); - int size = channelReduces.size() + gbStreams.size(); - // 回复级联的通道 - if (channelReduces.size() > 0) { - for (ChannelReduce channelReduce : channelReduces) { - DeviceChannel deviceChannel = storager.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId()); - cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size); - } - } - // 回复直播的通道 - if (gbStreams.size() > 0) { - for (GbStream gbStream : gbStreams) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(gbStream.getGbId()); - deviceChannel.setName(gbStream.getName()); - deviceChannel.setLongitude(gbStream.getLongitude()); - deviceChannel.setLatitude(gbStream.getLatitude()); - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId()); - deviceChannel.setManufacture("wvp-pro"); - deviceChannel.setStatus(gbStream.isStatus()?1:0); - // deviceChannel.setParentId(parentPlatform.getDeviceGBId()); - deviceChannel.setRegisterWay(1); - deviceChannel.setCivilCode(config.getDomain()); - deviceChannel.setModel("live"); - deviceChannel.setOwner("wvp-pro"); - deviceChannel.setParental(0); - deviceChannel.setSecrecy("0"); - deviceChannel.setSecrecy("0"); - - cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size); - } - } - if (size == 0) { - // 回复无通道 - cmderFroPlatform.catalogQuery(null, parentPlatform, sn, fromHeader.getTag(), size); - } - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } - - } -} 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 05e8bd71..98c1a96f 100644 --- 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 @@ -1,12 +1,18 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd; 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.event.EventPublisher; +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.utils.DateUtil; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,17 +26,27 @@ import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; +/** + * 状态信息(心跳)报送 + */ @Component public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + private Logger logger = LoggerFactory.getLogger(KeepaliveNotifyMessageHandler.class); - private final String cmdType = "Keepalive"; + private final static String cmdType = "Keepalive"; @Autowired private NotifyMessageHandler notifyMessageHandler; @Autowired - private EventPublisher publisher; + private IDeviceService deviceService; + + @Autowired + private UserSetting userSetting; + + @Autowired + private DynamicTask dynamicTask; @Override public void afterPropertiesSet() throws Exception { @@ -39,20 +55,47 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp @Override public void handForDevice(RequestEvent evt, Device device, Element element) { - // 检查设备是否存在并在线, 不在线则设置为在线 - try { - if (device != null ) { - // 回复200 OK - responseAck(evt, Response.OK); - publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE); - } - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); + if (device == null) { + // 未注册的设备不做处理 + return; } + SIPRequest request = (SIPRequest) evt.getRequest(); + // 回复200 OK + try { + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 心跳回复: {}", e.getMessage()); + } + + RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); + if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) { + device.setPort(remoteAddressInfo.getPort()); + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); + device.setIp(remoteAddressInfo.getIp()); + } + if (device.getKeepaliveTime() == null) { + device.setKeepaliveIntervalTime(60); + }else { + long lastTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(device.getKeepaliveTime()); + device.setKeepaliveIntervalTime(new Long(System.currentTimeMillis()/1000-lastTime).intValue()); + } + + device.setKeepaliveTime(DateUtil.getNow()); + + if (device.getOnline() == 1) { + deviceService.updateDevice(device); + }else { + // 对于已经离线的设备判断他的注册是否已经过期 + if (!deviceService.expire(device)){ + device.setOnline(0); + deviceService.online(device); + } + } + // 刷新过期任务 + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); + // 如果三次心跳失败,则设置设备离线 + dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId()), device.getKeepaliveIntervalTime()*1000*3); + } @Override 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 c61b7279..b15003cc 100644 --- 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 @@ -1,13 +1,20 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd; import com.genersoft.iot.vmp.common.StreamInfo; +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.SsrcTransaction; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; 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.notify.NotifyMessageHandler; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,11 +25,15 @@ import org.springframework.stereotype.Component; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; import javax.sip.SipException; +import javax.sip.header.CallIdHeader; import javax.sip.message.Response; import java.text.ParseException; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +/** + * 媒体通知 + */ @Component public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { @@ -35,9 +46,18 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i @Autowired private SIPCommander cmder; + @Autowired + private SIPCommanderFroPlatform sipCommanderFroPlatform; + @Autowired private IRedisCatchStorage redisCatchStorage; + @Autowired + private IVideoManagerStorage storage; + + @Autowired + private VideoStreamSessionManager sessionManager; + @Override public void afterPropertiesSet() throws Exception { notifyMessageHandler.addHandler(cmdType, this); @@ -48,21 +68,46 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i // 回复200 OK try { - responseAck(evt, Response.OK); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像流推送完毕,回复200OK: {}", e.getMessage()); } + CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME); String NotifyType =getText(rootElement, "NotifyType"); - if (NotifyType.equals("121")){ - logger.info("媒体播放完毕,通知关流"); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(device.getDeviceId(), "*"); + if ("121".equals(NotifyType)){ + logger.info("[录像流]推送完毕,收到关流通知"); + // 查询是设备 + StreamInfo streamInfo = redisCatchStorage.queryDownload(null, null, null, callIdHeader.getCallId()); if (streamInfo != null) { - redisCatchStorage.stopPlayback(streamInfo); - cmder.streamByeCmd(streamInfo.getDeviceID(), streamInfo.getChannelId()); + // 设置进度100% + streamInfo.setProgress(1); + redisCatchStorage.startDownload(streamInfo, callIdHeader.getCallId()); + } + + // 先从会话内查找 + SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null); + if (ssrcTransaction != null) { // 兼容海康 媒体通知 消息from字段不是设备ID的问题 + + try { + cmder.streamByeCmd(device, ssrcTransaction.getChannelId(), null, callIdHeader.getCallId()); + } catch (InvalidArgumentException | ParseException | SsrcTransactionNotFoundException | SipException e) { + logger.error("[录像流]推送完毕,收到关流通知, 发送BYE失败 {}", e.getMessage()); + } + + // 如果级联播放,需要给上级发送此通知 TODO 多个上级同时观看一个下级 可能存在停错的问题,需要将点播CallId进行上下级绑定 + SendRtpItem sendRtpItem = redisCatchStorage.querySendRTPServer(null, ssrcTransaction.getChannelId(), null, null); + if (sendRtpItem != null) { + ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); + if (parentPlatform == null) { + logger.warn("[级联消息发送]:发送MediaStatus发现上级平台{}不存在", sendRtpItem.getPlatformId()); + return; + } + try { + sipCommanderFroPlatform.sendMediaStatusNotify(parentPlatform, sendRtpItem); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 录像播放完毕: {}", e.getMessage()); + } + } } } } 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 ab7e5f63..40d1dcc9 100644 --- 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,33 +1,40 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.*; 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.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.GpsUtil; +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 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.util.StringUtils; +import org.springframework.util.ObjectUtils; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; +import java.util.concurrent.ConcurrentLinkedQueue; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +/** + * 移动设备位置数据通知,设备主动发起,不需要上级订阅 + */ @Component public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { @@ -38,10 +45,22 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen private NotifyMessageHandler notifyMessageHandler; @Autowired - private UserSetup userSetup; + private UserSetting userSetting; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceChannelService deviceChannelService; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; @Override public void afterPropertiesSet() throws Exception { @@ -51,48 +70,91 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(new SipMsgInfo(evt, device, rootElement)); + // 回复200 OK try { - rootElement = getRootElement(evt, device.getCharset()); + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 移动位置通知回复: {}", e.getMessage()); + } + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + SipMsgInfo sipMsgInfo = taskQueue.poll(); + try { + Element rootElementAfterCharset = getRootElement(sipMsgInfo.getEvt(), sipMsgInfo.getDevice().getCharset()); + if (rootElementAfterCharset == null) { + logger.warn("[移动位置通知] {}处理失败,未识别到信息体", device.getDeviceId()); + 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.setTime(getText(rootElementAfterCharset, "Time")); + mobilePosition.setLongitude(Double.parseDouble(getText(rootElementAfterCharset, "Longitude"))); + mobilePosition.setLatitude(Double.parseDouble(getText(rootElementAfterCharset, "Latitude"))); + if (NumericUtil.isDouble(getText(rootElementAfterCharset, "Speed"))) { + mobilePosition.setSpeed(Double.parseDouble(getText(rootElementAfterCharset, "Speed"))); + } else { + mobilePosition.setSpeed(0.0); + } + if (NumericUtil.isDouble(getText(rootElementAfterCharset, "Direction"))) { + mobilePosition.setDirection(Double.parseDouble(getText(rootElementAfterCharset, "Direction"))); + } else { + mobilePosition.setDirection(0.0); + } + if (NumericUtil.isDouble(getText(rootElementAfterCharset, "Altitude"))) { + mobilePosition.setAltitude(Double.parseDouble(getText(rootElementAfterCharset, "Altitude"))); + } else { + mobilePosition.setAltitude(0.0); + } + mobilePosition.setReportSource("Mobile Position"); - MobilePosition mobilePosition = new MobilePosition(); - if (!StringUtils.isEmpty(device.getName())) { - mobilePosition.setDeviceName(device.getName()); - } - mobilePosition.setDeviceId(device.getDeviceId()); - mobilePosition.setChannelId(getText(rootElement, "DeviceID")); - mobilePosition.setTime(getText(rootElement, "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"); - BaiduPoint bp = new BaiduPoint(); - bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude())); - logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat()); - mobilePosition.setGeodeticSystem("BD-09"); - mobilePosition.setCnLng(bp.getBdLng()); - mobilePosition.setCnLat(bp.getBdLat()); - if (!userSetup.getSavePositionHistory()) { - storager.clearMobilePositionsByDeviceId(device.getDeviceId()); - } - storager.insertMobilePosition(mobilePosition); - //回复 200 OK - responseAck(evt, Response.OK); - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { - e.printStackTrace(); + + // 更新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()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); + } + storager.updateChannelPosition(deviceChannel); + + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", mobilePosition.getTime()); + jsonObject.put("serial", deviceChannel.getDeviceId()); + jsonObject.put("code", deviceChannel.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); + + } catch (DocumentException e) { + e.printStackTrace(); + } catch (Exception e) { + logger.warn("[移动位置通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); + } + } + }); } } 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 60cf4d07..ce8c9397 100644 --- 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 @@ -2,17 +2,14 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query. import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.bean.Device; -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.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; 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.query.QueryMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,10 +20,8 @@ import org.springframework.stereotype.Component; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; import javax.sip.SipException; -import javax.sip.header.FromHeader; import javax.sip.message.Response; import java.text.ParseException; -import java.util.List; @Component public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { @@ -38,7 +33,7 @@ public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implemen private QueryMessageHandler queryMessageHandler; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private SIPCommanderFroPlatform cmderFroPlatform; @@ -64,13 +59,9 @@ public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implemen logger.info("不支持alarm查询"); try { - responseAck(evt, Response.NOT_FOUND, "not support alarm query"); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); + responseAck((SIPRequest) evt.getRequest(), Response.NOT_FOUND, "not support alarm query"); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.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 98ed2dc4..25a3df37 100644 --- 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 @@ -3,16 +3,14 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query. import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.bean.Device; 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.gb28181.event.EventPublisher; -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; 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.query.QueryMessageHandler; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +24,7 @@ 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; @Component @@ -38,7 +37,7 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem private QueryMessageHandler queryMessageHandler; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private SIPCommanderFroPlatform cmderFroPlatform; @@ -49,6 +48,9 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem @Autowired private EventPublisher publisher; + @Autowired + private IVideoManagerStorage storage; + @Override public void afterPropertiesSet() throws Exception { queryMessageHandler.addHandler(cmdType, this); @@ -62,59 +64,67 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem @Override public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { - String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + parentPlatform.getServerGBId(); FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); try { // 回复200 OK - responseAck(evt, Response.OK); - Element snElement = rootElement.element("SN"); - String sn = snElement.getText(); - // 准备回复通道信息 - List channelReduces = storager.queryChannelListInParentPlatform(parentPlatform.getServerGBId()); - // 查询关联的直播通道 - List gbStreams = storager.queryGbStreamListInPlatform(parentPlatform.getServerGBId()); - int size = channelReduces.size() + gbStreams.size(); - // 回复级联的通道 - if (channelReduces.size() > 0) { - for (ChannelReduce channelReduce : channelReduces) { - DeviceChannel deviceChannel = storager.queryChannel(channelReduce.getDeviceId(), channelReduce.getChannelId()); - cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size); - } - } - // 回复直播的通道 - if (gbStreams.size() > 0) { - for (GbStream gbStream : gbStreams) { - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setChannelId(gbStream.getGbId()); - deviceChannel.setName(gbStream.getName()); - deviceChannel.setLongitude(gbStream.getLongitude()); - deviceChannel.setLatitude(gbStream.getLatitude()); - deviceChannel.setDeviceId(parentPlatform.getDeviceGBId()); - deviceChannel.setManufacture("wvp-pro"); - deviceChannel.setStatus(gbStream.isStatus()?1:0); - // deviceChannel.setParentId(parentPlatform.getDeviceGBId()); - deviceChannel.setRegisterWay(1); - deviceChannel.setCivilCode(config.getDomain()); - deviceChannel.setModel("live"); - deviceChannel.setOwner("wvp-pro"); - deviceChannel.setParental(0); - deviceChannel.setSecrecy("0"); - deviceChannel.setSecrecy("0"); + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.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()); - cmderFroPlatform.catalogQuery(deviceChannel, parentPlatform, sn, fromHeader.getTag(), size); - } - } - if (size == 0) { + 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()); + }else { // 回复无通道 - cmderFroPlatform.catalogQuery(null, parentPlatform, sn, fromHeader.getTag(), size); + cmderFroPlatform.catalogQuery(null, parentPlatform, sn, fromHeader.getTag(), 0); } - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.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 8234cb15..0faf2945 100644 --- 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 @@ -6,6 +6,8 @@ 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.query.QueryMessageHandler; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,6 +22,8 @@ import javax.sip.header.FromHeader; import javax.sip.message.Response; import java.text.ParseException; +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + @Component public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { @@ -31,6 +35,8 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp @Autowired private SIPCommanderFroPlatform cmderFroPlatform; + @Autowired + private IVideoManagerStorage storager; @Override public void afterPropertiesSet() throws Exception { @@ -44,19 +50,29 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp @Override public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { - logger.info("接收到DeviceInfo查询消息"); + logger.info("[DeviceInfo查询]消息"); FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); try { // 回复200 OK - responseAck(evt, Response.OK); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage()); + return; } String sn = rootElement.element("SN").getText(); - cmderFroPlatform.deviceInfoResponse(parentPlatform, sn, fromHeader.getTag()); + /*根据WVP原有的数据结构,设备和通道是分开放置,设备信息都是存放在设备表里,通道表里的设备信息不可作为真实信息处理 + 大部分NVR/IPC设备对他的通道信息实现都是返回默认的值没有什么参考价值。NVR/IPC通道我们统一使用设备表的设备信息来作为返回。 + 我们这里使用查询数据库的方式来实现这个设备信息查询的功能,在其他地方对设备信息更新达到正确的目的。*/ + String channelId = getText(rootElement, "DeviceID"); + Device device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId); + if (device ==null){ + logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId); + return; + } + try { + cmderFroPlatform.deviceInfoResponse(parentPlatform,device, sn, fromHeader.getTag()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.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 c56151cd..e9d44d57 100644 --- 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 @@ -2,13 +2,15 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query. import com.genersoft.iot.vmp.conf.SipConfig; 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.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.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,6 +25,8 @@ import javax.sip.header.FromHeader; import javax.sip.message.Response; import java.text.ParseException; +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + @Component public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { @@ -33,7 +37,7 @@ public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent i private QueryMessageHandler queryMessageHandler; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private SIPCommanderFroPlatform cmderFroPlatform; @@ -61,15 +65,21 @@ public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent i FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME); // 回复200 OK try { - responseAck(evt, Response.OK); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 DeviceStatus查询回复200OK: {}", e.getMessage()); } String sn = rootElement.element("SN").getText(); - cmderFroPlatform.deviceStatusResponse(parentPlatform, sn, fromHeader.getTag()); + String channelId = getText(rootElement, "DeviceID"); + DeviceChannel deviceChannel = storager.queryChannelInParentPlatform(parentPlatform.getServerGBId(), channelId); + if (deviceChannel ==null){ + logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId); + return; + } + try { + cmderFroPlatform.deviceStatusResponse(parentPlatform,channelId, sn, fromHeader.getTag(),deviceChannel.getStatus()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.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 new file mode 100644 index 00000000..f3170a5d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java @@ -0,0 +1,146 @@ +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.event.record.RecordEndEventListener; +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.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 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.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.List; + +@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; + + @Autowired + private SIPCommanderFroPlatform cmderFroPlatform; + + @Autowired + private SIPCommander commander; + + @Autowired + private RecordEndEventListener recordEndEventListener; + + @Autowired + private SipConfig config; + + @Autowired + private EventPublisher publisher; + + @Override + public void afterPropertiesSet() throws Exception { + queryMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { + + SIPRequest request = (SIPRequest) evt.getRequest(); + Element snElement = rootElement.element("SN"); + int sn = Integer.parseInt(snElement.getText()); + Element deviceIDElement = rootElement.element("DeviceID"); + String channelId = deviceIDElement.getText(); + Element startTimeElement = rootElement.element("StartTime"); + String startTime = null; + if (startTimeElement != null) { + startTime = startTimeElement.getText(); + } + Element endTimeElement = rootElement.element("EndTime"); + String endTime = null; + if (endTimeElement != null) { + endTime = endTimeElement.getText(); + } + Element secrecyElement = rootElement.element("Secrecy"); + int secrecy = 0; + if (secrecyElement != null) { + secrecy = Integer.parseInt(secrecyElement.getText().trim()); + } + String type = "all"; + Element typeElement = rootElement.element("Type"); + 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 { + 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 { // 错误的请求 + try { + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.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 8492bd96..3edf02fe 100644 --- 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 @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; -import com.alibaba.fastjson.JSONObject; +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.transmit.callback.DeferredResultHolder; 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 ac946551..2e0d0716 100644 --- 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,6 +1,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; -import com.alibaba.fastjson.JSONObject; +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.transmit.callback.DeferredResultHolder; @@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP 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 gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +48,7 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i String channelId = getText(rootElement, "DeviceID"); String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId() + channelId; // 回复200 OK - responseAck(evt, Response.OK); + responseAck((SIPRequest) evt.getRequest(), Response.OK); // 此处是对本平台发出Broadcast指令的应答 JSONObject json = new JSONObject(); XmlUtil.node2Json(rootElement, json); @@ -61,7 +62,7 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i } catch (ParseException | SipException | InvalidArgumentException e) { - e.printStackTrace(); + logger.error("[命令发送失败] 国标级联 语音喊话: {}", e.getMessage()); } } 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 5fcc3ada..761481be 100644 --- 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 @@ -1,25 +1,21 @@ 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.conf.SipConfig; -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.DeviceOffLineDetector; -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; +import com.genersoft.iot.vmp.gb28181.bean.*; +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.NumericUtil; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; 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 javax.sip.InvalidArgumentException; @@ -30,9 +26,11 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; -import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; - +/** + * 目录查询的回复 + */ @Component public class CatalogResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { @@ -42,20 +40,17 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp @Autowired private ResponseMessageHandler responseMessageHandler; - @Autowired - private IVideoManagerStorager storager; + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Autowired - private DeferredResultHolder deferredResultHolder; + private IVideoManagerStorage storager; @Autowired - private DeviceOffLineDetector offLineDetector; + private CatalogDataCatch catalogDataCatch; + @Qualifier("taskExecutor") @Autowired - private SipConfig config; - - @Autowired - private EventPublisher publisher; + private ThreadPoolTaskExecutor taskExecutor; @Override public void afterPropertiesSet() throws Exception { @@ -64,122 +59,110 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp @Override public void handForDevice(RequestEvent evt, Device device, Element element) { - String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + device.getDeviceId(); - Element rootElement = null; + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(new HandlerCatchData(evt, device, element)); + // 回复200 OK try { - rootElement = getRootElement(evt, device.getCharset()); - Element deviceListElement = rootElement.element("DeviceList"); - Iterator deviceListIterator = deviceListElement.elementIterator(); - if (deviceListIterator != null) { - List channelList = new ArrayList<>(); - // 遍历DeviceList - while (deviceListIterator.hasNext()) { - Element itemDevice = deviceListIterator.next(); - Element channelDeviceElement = itemDevice.element("DeviceID"); - if (channelDeviceElement == null) { - continue; - } - String channelDeviceId = channelDeviceElement.getText(); - Element channdelNameElement = itemDevice.element("Name"); - String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : ""; - Element statusElement = itemDevice.element("Status"); - String status = statusElement != null ? statusElement.getText().toString() : "ON"; - DeviceChannel deviceChannel = new DeviceChannel(); - deviceChannel.setName(channelName); - deviceChannel.setChannelId(channelDeviceId); - // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 - if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) { - deviceChannel.setStatus(1); - } - if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { - deviceChannel.setStatus(0); - } - - deviceChannel.setManufacture(getText(itemDevice, "Manufacturer")); - deviceChannel.setModel(getText(itemDevice, "Model")); - deviceChannel.setOwner(getText(itemDevice, "Owner")); - deviceChannel.setCivilCode(getText(itemDevice, "CivilCode")); - deviceChannel.setBlock(getText(itemDevice, "Block")); - deviceChannel.setAddress(getText(itemDevice, "Address")); - if (getText(itemDevice, "Parental") == null || getText(itemDevice, "Parental") == "") { - deviceChannel.setParental(0); - } else { - deviceChannel.setParental(Integer.parseInt(getText(itemDevice, "Parental"))); - } - deviceChannel.setParentId(getText(itemDevice, "ParentID")); - if (getText(itemDevice, "SafetyWay") == null || getText(itemDevice, "SafetyWay") == "") { - deviceChannel.setSafetyWay(0); - } else { - deviceChannel.setSafetyWay(Integer.parseInt(getText(itemDevice, "SafetyWay"))); - } - if (getText(itemDevice, "RegisterWay") == null || getText(itemDevice, "RegisterWay") == "") { - deviceChannel.setRegisterWay(1); - } else { - deviceChannel.setRegisterWay(Integer.parseInt(getText(itemDevice, "RegisterWay"))); - } - deviceChannel.setCertNum(getText(itemDevice, "CertNum")); - if (getText(itemDevice, "Certifiable") == null || getText(itemDevice, "Certifiable") == "") { - deviceChannel.setCertifiable(0); - } else { - deviceChannel.setCertifiable(Integer.parseInt(getText(itemDevice, "Certifiable"))); - } - if (getText(itemDevice, "ErrCode") == null || getText(itemDevice, "ErrCode") == "") { - deviceChannel.setErrCode(0); - } else { - deviceChannel.setErrCode(Integer.parseInt(getText(itemDevice, "ErrCode"))); - } - deviceChannel.setEndTime(getText(itemDevice, "EndTime")); - deviceChannel.setSecrecy(getText(itemDevice, "Secrecy")); - deviceChannel.setIpAddress(getText(itemDevice, "IPAddress")); - if (getText(itemDevice, "Port") == null || getText(itemDevice, "Port") == "") { - deviceChannel.setPort(0); - } else { - deviceChannel.setPort(Integer.parseInt(getText(itemDevice, "Port"))); - } - deviceChannel.setPassword(getText(itemDevice, "Password")); - if (NumericUtil.isDouble(getText(itemDevice, "Longitude"))) { - deviceChannel.setLongitude(Double.parseDouble(getText(itemDevice, "Longitude"))); - } else { - deviceChannel.setLongitude(0.00); - } - if (NumericUtil.isDouble(getText(itemDevice, "Latitude"))) { - deviceChannel.setLatitude(Double.parseDouble(getText(itemDevice, "Latitude"))); - } else { - deviceChannel.setLatitude(0.00); - } - if (getText(itemDevice, "PTZType") == null || getText(itemDevice, "PTZType") == "") { - deviceChannel.setPTZType(0); - } else { - deviceChannel.setPTZType(Integer.parseInt(getText(itemDevice, "PTZType"))); - } - deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC - // TODO 修改为批量插入 - channelList.add(deviceChannel); - } - storager.updateChannels(device.getDeviceId(), channelList); - RequestMessage msg = new RequestMessage(); - msg.setKey(key); - msg.setData(device); - deferredResultHolder.invokeAllResult(msg); - // 回复200 OK - responseAck(evt, Response.OK); - if (offLineDetector.isOnline(device.getDeviceId())) { - publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE); - } - } - } catch (DocumentException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } catch (SipException e) { - e.printStackTrace(); + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 目录查询回复: {}", e.getMessage()); } + // 如果不为空则说明已经开启消息处理 + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + // 全局异常捕获,保证下一条可以得到处理 + try { + HandlerCatchData take = taskQueue.poll(); + Element rootElement = null; + try { + rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset()); + } catch (DocumentException e) { + logger.error("[xml解析] 失败: ", e); + continue; + } + if (rootElement == null) { + logger.warn("[ 收到通道 ] content cannot be null, {}", evt.getRequest()); + continue; + } + Element deviceListElement = rootElement.element("DeviceList"); + Element sumNumElement = rootElement.element("SumNum"); + Element snElement = rootElement.element("SN"); + int sumNum = Integer.parseInt(sumNumElement.getText()); + + if (sumNum == 0) { + logger.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId()); + // 数据已经完整接收 + storager.cleanChannelsForDevice(take.getDevice().getDeviceId()); + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null); + } else { + Iterator deviceListIterator = deviceListElement.elementIterator(); + if (deviceListIterator != null) { + List channelList = new ArrayList<>(); + // 遍历DeviceList + while (deviceListIterator.hasNext()) { + Element itemDevice = deviceListIterator.next(); + Element channelDeviceElement = itemDevice.element("DeviceID"); + if (channelDeviceElement == null) { + continue; + } + DeviceChannel deviceChannel = XmlUtil.channelContentHander(itemDevice, device, null); + deviceChannel.setDeviceId(take.getDevice().getDeviceId()); + + channelList.add(deviceChannel); + } + 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) { + // 数据已经完整接收, 此时可能存在某个设备离线变上线的情况,但是考虑到性能,此处不做处理, + // 目前支持设备通道上线通知时和设备上线时向上级通知 + boolean resetChannelsResult = storager.resetChannels(take.getDevice().getDeviceId(), catalogDataCatch.get(take.getDevice().getDeviceId())); + if (!resetChannelsResult) { + String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.get(take.getDevice().getDeviceId()).size() + "条"; + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg); + } else { + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null); + } + } + } + + } + }catch (Exception e) { + logger.warn("[收到通道] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); + } + } + }); + } + } @Override public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) { } + + public SyncStatus getChannelSyncProgress(String deviceId) { + if (catalogDataCatch.get(deviceId) == null) { + return null; + } else { + return catalogDataCatch.getSyncStatus(deviceId); + } + } + + public boolean isSyncRunning(String deviceId) { + if (catalogDataCatch.get(deviceId) == null) { + return false; + } else { + return catalogDataCatch.isSyncRunning(deviceId); + } + } + + public void setChannelSyncReady(Device device, int sn) { + catalogDataCatch.addReady(device, sn); + } + + public void setChannelSyncEnd(String deviceId, String errorMsg) { + catalogDataCatch.setChannelSyncEnd(deviceId, errorMsg); + } } 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 d1a2893f..47ae1a9c 100644 --- 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 @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; -import com.alibaba.fastjson.JSONObject; +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; @@ -10,6 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP 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 gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,24 +53,21 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + device.getDeviceId() + channelId; try { // 回复200 OK - responseAck(evt, Response.OK); - // 此处是对本平台发出DeviceControl指令的应答 - JSONObject json = new JSONObject(); - XmlUtil.node2Json(element, json); - if (logger.isDebugEnabled()) { - logger.debug(json.toJSONString()); - } - RequestMessage msg = new RequestMessage(); - msg.setKey(key); - msg.setData(json); - deferredResultHolder.invokeAllResult(msg); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 设备配置查询: {}", e.getMessage()); } + // 此处是对本平台发出DeviceControl指令的应答 + JSONObject json = new JSONObject(); + XmlUtil.node2Json(element, json); + if (logger.isDebugEnabled()) { + logger.debug(json.toJSONString()); + } + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(json); + deferredResultHolder.invokeAllResult(msg); + } 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 68ffe394..b18a87b2 100644 --- 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 @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; -import com.alibaba.fastjson.JSONObject; +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.transmit.callback.DeferredResultHolder; 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 69f664bb..af08e13b 100644 --- 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 @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; -import com.alibaba.fastjson.JSONObject; +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.transmit.callback.DeferredResultHolder; @@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP 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 gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,7 +17,12 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +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; @@ -40,6 +46,11 @@ public class DeviceControlResponseMessageHandler extends SIPRequestProcessorPare @Override public void handForDevice(RequestEvent evt, Device device, Element element) { // 此处是对本平台发出DeviceControl指令的应答 + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 设备控制: {}", e.getMessage()); + } JSONObject json = new JSONObject(); String channelId = getText(element, "DeviceID"); XmlUtil.node2Json(element, json); @@ -51,6 +62,7 @@ public class DeviceControlResponseMessageHandler extends SIPRequestProcessorPare msg.setKey(key); msg.setData(json); deferredResultHolder.invokeAllResult(msg); + } @Override 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 78fc9361..496fd940 100644 --- 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 @@ -4,14 +4,16 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; 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.DeviceOffLineDetector; 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; 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.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.DocumentException; import org.dom4j.Element; import org.slf4j.Logger; @@ -19,7 +21,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; @@ -29,6 +31,9 @@ import java.text.ParseException; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +/** + * @author lin + */ @Component public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { @@ -38,20 +43,12 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent @Autowired private ResponseMessageHandler responseMessageHandler; - @Autowired - private IVideoManagerStorager storager; - @Autowired private DeferredResultHolder deferredResultHolder; - @Autowired - private DeviceOffLineDetector offLineDetector; @Autowired - private SipConfig config; - - @Autowired - private EventPublisher publisher; + private IDeviceService deviceService; @Override public void afterPropertiesSet() throws Exception { @@ -61,8 +58,24 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { logger.debug("接收到DeviceInfo应答消息"); + // 检查设备是否存在, 不存在则不回复 + if (device == null || device.getOnline() == 0) { + logger.warn("[接收到DeviceInfo应答消息,但是设备已经离线]:" + (device != null ? device.getDeviceId():"" )); + return; + } + SIPRequest request = (SIPRequest) evt.getRequest(); try { rootElement = getRootElement(evt, device.getCharset()); + + if (rootElement == null) { + logger.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()); + } + return; + } Element deviceIdElement = rootElement.element("DeviceID"); String channelId = deviceIdElement.getTextTrim(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICEINFO + device.getDeviceId() + channelId; @@ -71,29 +84,25 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent device.setManufacturer(getText(rootElement, "Manufacturer")); device.setModel(getText(rootElement, "Model")); device.setFirmware(getText(rootElement, "Firmware")); - if (StringUtils.isEmpty(device.getStreamMode())) { + if (ObjectUtils.isEmpty(device.getStreamMode())) { device.setStreamMode("UDP"); } - storager.updateDevice(device); + deviceService.updateDevice(device); RequestMessage msg = new RequestMessage(); msg.setKey(key); msg.setData(device); deferredResultHolder.invokeAllResult(msg); - // 回复200 OK - responseAck(evt, Response.OK); - if (offLineDetector.isOnline(device.getDeviceId())) { - publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE); - } } catch (DocumentException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } catch (SipException e) { - e.printStackTrace(); + throw new RuntimeException(e); } + try { + // 回复200 OK + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] DeviceInfo应答消息 200: {}", e.getMessage()); + } + } @Override 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 1a7ab643..0c0c71fb 100644 --- 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 @@ -1,10 +1,9 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.event.DeviceOffLineDetector; 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; @@ -12,6 +11,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP 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.storager.IRedisCatchStorage; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +26,7 @@ import javax.sip.RequestEvent; import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; +import java.util.Objects; @Component public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { @@ -34,14 +37,14 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen @Autowired private ResponseMessageHandler responseMessageHandler; - @Autowired - private DeviceOffLineDetector offLineDetector; - @Autowired private DeferredResultHolder deferredResultHolder; @Autowired - private EventPublisher publisher; + private IDeviceService deviceService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; @Override public void afterPropertiesSet() throws Exception { @@ -52,33 +55,33 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen public void handForDevice(RequestEvent evt, Device device, Element element) { logger.info("接收到DeviceStatus应答消息"); // 检查设备是否存在, 不存在则不回复 + if (device == null) { + return; + } // 回复200 OK try { - responseAck(evt, Response.OK); - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.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()); } + String text = onlineElement.getText(); + if ("ONLINE".equalsIgnoreCase(text.trim())) { + deviceService.online(device); + }else { + deviceService.offline(device.getDeviceId()); + } RequestMessage msg = new RequestMessage(); - msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId() + channelId); + msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId()); msg.setData(json); deferredResultHolder.invokeAllResult(msg); - - if (offLineDetector.isOnline(device.getDeviceId())) { - publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE); - } else { - - } } @Override 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 b456386f..226799a4 100644 --- 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,16 +1,19 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.BaiduPoint; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.*; 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.Coordtransform; import com.genersoft.iot.vmp.gb28181.utils.NumericUtil; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +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 com.genersoft.iot.vmp.utils.GpsUtil; +import gov.nist.javax.sip.message.SIPRequest; import org.dom4j.DocumentException; import org.dom4j.Element; import org.slf4j.Logger; @@ -18,7 +21,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; @@ -28,6 +31,10 @@ import java.text.ParseException; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +/** + * 移动设备位置数据查询回复 + * @author lin + */ @Component public class MobilePositionResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { @@ -38,10 +45,16 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar private ResponseMessageHandler responseMessageHandler; @Autowired - private UserSetup userSetup; + private UserSetting userSetting; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IDeviceChannelService deviceChannelService; @Override public void afterPropertiesSet() throws Exception { @@ -50,12 +63,22 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + SIPRequest request = (SIPRequest) evt.getRequest(); try { rootElement = getRootElement(evt, device.getCharset()); - + if (rootElement == null) { + logger.warn("[ 移动设备位置数据查询回复 ] content cannot be null, {}", evt.getRequest()); + try { + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 移动设备位置数据查询 BAD_REQUEST: {}", e.getMessage()); + } + return; + } MobilePosition mobilePosition = new MobilePosition(); - if (!StringUtils.isEmpty(device.getName())) { + mobilePosition.setCreateTime(DateUtil.getNow()); + if (!ObjectUtils.isEmpty(device.getName())) { mobilePosition.setDeviceName(device.getName()); } mobilePosition.setDeviceId(device.getDeviceId()); @@ -79,19 +102,46 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar mobilePosition.setAltitude(0.0); } mobilePosition.setReportSource("Mobile Position"); - BaiduPoint bp = new BaiduPoint(); - bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude())); - logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat()); - mobilePosition.setGeodeticSystem("BD-09"); - mobilePosition.setCnLng(bp.getBdLng()); - mobilePosition.setCnLat(bp.getBdLat()); - if (!userSetup.getSavePositionHistory()) { - storager.clearMobilePositionsByDeviceId(device.getDeviceId()); + + // 更新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()); + + if (userSetting.getSavePositionHistory()) { + storager.insertMobilePosition(mobilePosition); } - storager.insertMobilePosition(mobilePosition); + storager.updateChannelPosition(deviceChannel); + + // 发送redis消息。 通知位置信息的变化 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("time", mobilePosition.getTime()); + jsonObject.put("serial", deviceChannel.getDeviceId()); + jsonObject.put("code", deviceChannel.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); //回复 200 OK - responseAck(evt, Response.OK); - } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) { + try { + responseAck(request, Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 移动设备位置数据查询 200: {}", e.getMessage()); + } + + } catch (DocumentException e) { e.printStackTrace(); } } 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 new file mode 100644 index 00000000..862a8eea --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java @@ -0,0 +1,120 @@ +package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; + +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 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; + +import javax.sip.InvalidArgumentException; +import javax.sip.RequestEvent; +import javax.sip.SipException; +import javax.sip.message.Response; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; + +/** + * 设备预置位查询应答 + */ +@Component +public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { + + private Logger logger = LoggerFactory.getLogger(PresetQueryResponseMessageHandler.class); + private final String cmdType = "PresetQuery"; + + @Autowired + private ResponseMessageHandler responseMessageHandler; + + @Autowired + private DeferredResultHolder deferredResultHolder; + + + @Override + public void afterPropertiesSet() throws Exception { + responseMessageHandler.addHandler(cmdType, this); + } + + @Override + public void handForDevice(RequestEvent evt, Device device, Element element) { + + SIPRequest request = (SIPRequest) evt.getRequest(); + + try { + Element rootElement = getRootElement(evt, device.getCharset()); + + if (rootElement == null) { + logger.warn("[ 设备预置位查询应答 ] content cannot be null, {}", evt.getRequest()); + try { + responseAck(request, Response.BAD_REQUEST); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); + } + return; + } + Element presetListNumElement = rootElement.element("PresetList"); + Element snElement = rootElement.element("SN"); + //该字段可能为通道或则设备的id + String deviceId = getText(rootElement, "DeviceID"); + String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + deviceId; + if (snElement == null || presetListNumElement == null) { + try { + responseAck(request, Response.BAD_REQUEST, "xml error"); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); + } + return; + } + int sumNum = Integer.parseInt(presetListNumElement.attributeValue("Num")); + List presetQuerySipReqList = new ArrayList<>(); + if (sumNum > 0) { + for (Iterator presetIterator = presetListNumElement.elementIterator(); presetIterator.hasNext(); ) { + Element itemListElement = presetIterator.next(); + PresetQuerySipReq presetQuerySipReq = new PresetQuerySipReq(); + for (Iterator itemListIterator = itemListElement.elementIterator(); itemListIterator.hasNext(); ) { + // 遍历item + Element itemOne = itemListIterator.next(); + String name = itemOne.getName(); + String textTrim = itemOne.getTextTrim(); + if ("PresetID".equalsIgnoreCase(name)) { + presetQuerySipReq.setPresetId(textTrim); + } else { + presetQuerySipReq.setPresetName(textTrim); + } + } + presetQuerySipReqList.add(presetQuerySipReq); + } + } + RequestMessage requestMessage = new RequestMessage(); + requestMessage.setKey(key); + requestMessage.setData(presetQuerySipReqList); + deferredResultHolder.invokeAllResult(requestMessage); + try { + responseAck(request, Response.OK); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage()); + } + } catch (DocumentException e) { + logger.error("[解析xml]失败: ", e); + } + } + + @Override + public void handForPlatform(RequestEvent evt, ParentPlatform 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 f0f84213..11d239ef 100644 --- 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,24 +1,25 @@ 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.RecordInfo; -import com.genersoft.iot.vmp.gb28181.bean.RecordItem; -import com.genersoft.iot.vmp.gb28181.transmit.callback.CheckForAllRecordsThread; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.session.RecordDataCatch; 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.DateUtil; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import com.genersoft.iot.vmp.utils.DateUtil; +import gov.nist.javax.sip.message.SIPRequest; 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.util.ObjectUtils; import javax.sip.InvalidArgumentException; import javax.sip.RequestEvent; @@ -26,29 +27,40 @@ import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedQueue; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; +/** + * @author lin + */ @Component public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler { - private Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class); - public static volatile List threadNameList = new ArrayList(); + private final Logger logger = LoggerFactory.getLogger(RecordInfoResponseMessageHandler.class); private final String cmdType = "RecordInfo"; - private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_"; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Autowired private ResponseMessageHandler responseMessageHandler; @Autowired - private RedisUtil redis; + private RecordDataCatch recordDataCatch; @Autowired private DeferredResultHolder deferredResultHolder; + @Autowired + private EventPublisher eventPublisher; + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + @Override public void afterPropertiesSet() throws Exception { responseMessageHandler.addHandler(cmdType, this); @@ -56,91 +68,92 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { - - // 回复200 OK + boolean isEmpty = taskQueue.isEmpty(); try { - responseAck(evt, Response.OK); - - rootElement = getRootElement(evt, device.getCharset()); - String uuid = UUID.randomUUID().toString().replace("-", ""); - RecordInfo recordInfo = new RecordInfo(); - String sn = getText(rootElement, "SN"); - String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + device.getDeviceId() + sn; - recordInfo.setDeviceId(device.getDeviceId()); - recordInfo.setSn(sn); - recordInfo.setName(getText(rootElement, "Name")); - if (getText(rootElement, "SumNum") == null || getText(rootElement, "SumNum") == "") { - recordInfo.setSumNum(0); - } else { - recordInfo.setSumNum(Integer.parseInt(getText(rootElement, "SumNum"))); - } - Element recordListElement = rootElement.element("RecordList"); - if (recordListElement == null || recordInfo.getSumNum() == 0) { - logger.info("无录像数据"); - RequestMessage msg = new RequestMessage(); - msg.setKey(key); - msg.setData(recordInfo); - deferredResultHolder.invokeAllResult(msg); - } else { - Iterator recordListIterator = recordListElement.elementIterator(); - List recordList = new ArrayList(); - if (recordListIterator != null) { - RecordItem record = new RecordItem(); - logger.info("处理录像列表数据..."); - // 遍历DeviceList - while (recordListIterator.hasNext()) { - Element itemRecord = recordListIterator.next(); - Element recordElement = itemRecord.element("DeviceID"); - if (recordElement == null) { - logger.info("记录为空,下一个..."); + // 回复200 OK + responseAck((SIPRequest) evt.getRequest(), Response.OK); + }catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage()); + } + taskQueue.offer(new HandlerCatchData(evt, device, rootElement)); + if (isEmpty) { + taskExecutor.execute(()->{ + while (!taskQueue.isEmpty()) { + try { + HandlerCatchData take = taskQueue.poll(); + Element rootElementForCharset = getRootElement(take.getEvt(), take.getDevice().getCharset()); + if (rootElement == null) { + logger.warn("[ 国标录像 ] content cannot be null, {}", evt.getRequest()); continue; } - record = new RecordItem(); - record.setDeviceId(getText(itemRecord, "DeviceID")); - record.setName(getText(itemRecord, "Name")); - record.setFilePath(getText(itemRecord, "FilePath")); - record.setAddress(getText(itemRecord, "Address")); - record.setStartTime( - DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "StartTime"))); - record.setEndTime( - DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(getText(itemRecord, "EndTime"))); - record.setSecrecy(itemRecord.element("Secrecy") == null ? 0 - : Integer.parseInt(getText(itemRecord, "Secrecy"))); - record.setType(getText(itemRecord, "Type")); - record.setRecorderId(getText(itemRecord, "RecorderID")); - recordList.add(record); - } - recordInfo.setRecordList(recordList); - } + String sn = getText(rootElementForCharset, "SN"); + String channelId = getText(rootElementForCharset, "DeviceID"); + RecordInfo recordInfo = new RecordInfo(); + recordInfo.setChannelId(channelId); + recordInfo.setDeviceId(take.getDevice().getDeviceId()); + recordInfo.setSn(sn); + recordInfo.setName(getText(rootElementForCharset, "Name")); + String sumNumStr = getText(rootElementForCharset, "SumNum"); + int sumNum = 0; + if (!ObjectUtils.isEmpty(sumNumStr)) { + sumNum = Integer.parseInt(sumNumStr); + } + recordInfo.setSumNum(sumNum); + Element recordListElement = rootElementForCharset.element("RecordList"); + if (recordListElement == null || sumNum == 0) { + logger.info("无录像数据"); + eventPublisher.recordEndEventPush(recordInfo); + recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, new ArrayList<>()); + releaseRequest(take.getDevice().getDeviceId(), sn); + } else { + Iterator recordListIterator = recordListElement.elementIterator(); + if (recordListIterator != null) { + List recordList = new ArrayList<>(); + // 遍历DeviceList + while (recordListIterator.hasNext()) { + Element itemRecord = recordListIterator.next(); + Element recordElement = itemRecord.element("DeviceID"); + if (recordElement == null) { + logger.info("记录为空,下一个..."); + continue; + } + RecordItem record = new RecordItem(); + record.setDeviceId(getText(itemRecord, "DeviceID")); + record.setName(getText(itemRecord, "Name")); + record.setFilePath(getText(itemRecord, "FilePath")); + record.setFileSize(getText(itemRecord, "FileSize")); + record.setAddress(getText(itemRecord, "Address")); - // 改用单独线程统计已获取录像文件数量,避免多包并行分别统计不完整的问题 - String cacheKey = CACHE_RECORDINFO_KEY + device.getDeviceId() + sn; - redis.set(cacheKey + "_" + uuid, recordList, 90); - if (!threadNameList.contains(cacheKey)) { - threadNameList.add(cacheKey); - CheckForAllRecordsThread chk = new CheckForAllRecordsThread(cacheKey, recordInfo); - chk.setName(cacheKey); - chk.setDeferredResultHolder(deferredResultHolder); - chk.setRedis(redis); - chk.setLogger(logger); - chk.start(); - if (logger.isDebugEnabled()) { - logger.debug("Start Thread " + cacheKey + "."); - } - } else { - if (logger.isDebugEnabled()) { - logger.debug("Thread " + cacheKey + " already started."); + String startTimeStr = getText(itemRecord, "StartTime"); + record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(startTimeStr)); + + String endTimeStr = getText(itemRecord, "EndTime"); + record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTimeStr)); + + record.setSecrecy(itemRecord.element("Secrecy") == null ? 0 + : Integer.parseInt(getText(itemRecord, "Secrecy"))); + record.setType(getText(itemRecord, "Type")); + record.setRecorderId(getText(itemRecord, "RecorderID")); + recordList.add(record); + } + recordInfo.setRecordList(recordList); + // 发送消息,如果是上级查询此录像,则会通过这里通知给上级 + eventPublisher.recordEndEventPush(recordInfo); + int count = recordDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, recordList); + logger.info("[国标录像], {}->{}: {}/{}", take.getDevice().getDeviceId(), sn, count, sumNum); + } + + if (recordDataCatch.isComplete(take.getDevice().getDeviceId(), sn)){ + releaseRequest(take.getDevice().getDeviceId(), sn); + } + } + } catch (DocumentException e) { + logger.error("xml解析异常: ", e); + } catch (Exception e) { + logger.warn("[国标录像] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); } } - } - } catch (SipException e) { - e.printStackTrace(); - } catch (InvalidArgumentException e) { - e.printStackTrace(); - } catch (ParseException e) { - e.printStackTrace(); - } catch (DocumentException e) { - e.printStackTrace(); + }); } } @@ -148,4 +161,16 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element element) { } + + public void releaseRequest(String deviceId, String sn){ + String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; + // 对数据进行排序 + Collections.sort(recordDataCatch.getRecordInfo(deviceId, sn).getRecordList()); + + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setData(recordDataCatch.getRecordInfo(deviceId, sn)); + deferredResultHolder.invokeAllResult(msg); + recordDataCatch.remove(deviceId, sn); + } } 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 64933b80..ff63fad0 100644 --- 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 @@ -17,7 +17,7 @@ import javax.sip.ResponseEvent; @Component public class ByeResponseProcessor extends SIPResponseProcessorAbstract { - private String method = "BYE"; + private final String method = "BYE"; @Autowired private SipLayer sipLayer; 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 80d7e2b2..775beeb6 100644 --- 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 @@ -17,7 +17,7 @@ import javax.sip.ResponseEvent; @Component public class CancelResponseProcessor extends SIPResponseProcessorAbstract { - private String method = "CANCEL"; + private final String method = "CANCEL"; @Autowired private SipLayer sipLayer; 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 5446a902..d0ba97eb 100644 --- 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 @@ -2,21 +2,38 @@ 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.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; +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.transmit.event.response.SIPResponseProcessorAbstract; +import com.genersoft.iot.vmp.gb28181.utils.SipUtils; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.utils.GitUtil; import gov.nist.javax.sip.ResponseEventExt; +import gov.nist.javax.sip.SipProviderImpl; +import gov.nist.javax.sip.message.SIPResponse; +import gov.nist.javax.sip.stack.SIPClientTransaction; import gov.nist.javax.sip.stack.SIPDialog; +import gov.nist.javax.sip.stack.SIPTransaction; +import gov.nist.javax.sip.stack.SIPTransactionImpl; 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.stereotype.Component; -import javax.sip.InvalidArgumentException; -import javax.sip.ResponseEvent; -import javax.sip.SipException; +import javax.sdp.SdpFactory; +import javax.sdp.SdpParseException; +import javax.sdp.SessionDescription; +import javax.sip.*; +import javax.sip.address.Address; import javax.sip.address.SipURI; import javax.sip.header.CSeqHeader; +import javax.sip.header.UserAgentHeader; import javax.sip.message.Request; import javax.sip.message.Response; import java.text.ParseException; @@ -31,17 +48,21 @@ import java.text.ParseException; public class InviteResponseProcessor extends SIPResponseProcessorAbstract { private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class); - private String method = "INVITE"; + private final String method = "INVITE"; + + @Autowired + private SIPProcessorObserver sipProcessorObserver; + @Autowired private SipLayer sipLayer; @Autowired - private SipConfig config; - + private SIPSender sipSender; @Autowired - private SIPProcessorObserver sipProcessorObserver; + private SIPRequestHeaderProvider headerProvider; + @Override public void afterPropertiesSet() throws Exception { @@ -49,8 +70,7 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract { sipProcessorObserver.addResponseProcessor(method, this); } - @Autowired - private VideoStreamSessionManager streamSession; + /** * 处理invite响应 @@ -61,7 +81,8 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract { @Override public void process(ResponseEvent evt ){ try { - Response response = evt.getResponse(); + + SIPResponse response = (SIPResponse)evt.getResponse(); int statusCode = response.getStatusCode(); // trying不会回复 if (statusCode == Response.TRYING) { @@ -70,27 +91,28 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract { // 下发ack if (statusCode == Response.OK) { ResponseEventExt event = (ResponseEventExt)evt; - SIPDialog dialog = (SIPDialog)evt.getDialog(); - CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME); - Request reqAck = dialog.createAck(cseq.getSeqNumber()); - SipURI requestURI = (SipURI) reqAck.getRequestURI(); - try { - requestURI.setHost(event.getRemoteIpAddress()); - } catch (ParseException e) { - e.printStackTrace(); + + String contentString = new String(response.getRawContent()); + // jainSip不支持y=字段, 移除以解析。 + int ssrcIndex = contentString.indexOf("y="); + // 检查是否有y字段 + SessionDescription sdp; + if (ssrcIndex >= 0) { + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 + String substring = contentString.substring(0, contentString.indexOf("y=")); + sdp = SdpFactory.getInstance().createSessionDescription(substring); + } else { + sdp = SdpFactory.getInstance().createSessionDescription(contentString); } - requestURI.setPort(event.getRemotePort()); - reqAck.setRequestURI(requestURI); - logger.info("向 " + event.getRemoteIpAddress() + ":" + event.getRemotePort() + "回复ack"); - SipURI sipURI = (SipURI)dialog.getRemoteParty().getURI(); - String deviceId = requestURI.getUser(); - String channelId = sipURI.getUser(); - dialog.sendAck(reqAck); + SipURI requestUri = sipLayer.getSipFactory().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()); + sipSender.transmitRequest( response.getLocalAddress().getHostAddress(), reqAck); } - } catch (InvalidArgumentException | SipException e) { - e.printStackTrace(); + } catch (InvalidArgumentException | ParseException | SipException | SdpParseException e) { + logger.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 a5dced37..14d1f84b 100644 --- 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 @@ -2,20 +2,26 @@ 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.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.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import javax.sip.InvalidArgumentException; import javax.sip.ResponseEvent; +import javax.sip.SipException; import javax.sip.header.CallIdHeader; import javax.sip.header.WWWAuthenticateHeader; import javax.sip.message.Response; +import java.text.ParseException; /** * @description:Register响应处理器 @@ -25,14 +31,14 @@ import javax.sip.message.Response; @Component public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { - private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class); - private String method = "REGISTER"; + private final Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class); + private final String method = "REGISTER"; @Autowired private ISIPCommanderForPlatform sipCommanderForPlatform; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private IRedisCatchStorage redisCatchStorage; @@ -40,6 +46,12 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { @Autowired private SIPProcessorObserver sipProcessorObserver; + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private IPlatformService platformService; + @Override public void afterPropertiesSet() throws Exception { // 添加消息处理的订阅 @@ -56,48 +68,43 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract { Response response = evt.getResponse(); CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME); String callId = callIdHeader.getCallId(); - - String platformGBId = redisCatchStorage.queryPlatformRegisterInfo(callId); - if (platformGBId == null) { - logger.info(String.format("未找到callId: %s 的注册/注销平台id", callId )); + PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId); + if (platformRegisterInfo == null) { + logger.info(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId )); return; } - ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformGBId); + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformRegisterInfo.getPlatformId()); if (parentPlatformCatch == null) { - logger.warn(String.format("收到 %s 的注册/注销%S请求, 但是平台缓存信息未查询到!!!", platformGBId, response.getStatusCode())); + logger.warn(String.format("[国标级联]收到注册/注销%S请求,平台:%s,但是平台缓存信息未查询到!!!", response.getStatusCode(),platformRegisterInfo.getPlatformId())); return; } - String action = parentPlatformCatch.getParentPlatform().getExpires().equals("0") ? "注销" : "注册"; - logger.info(String.format("收到 %s %s的%S响应", platformGBId, action, response.getStatusCode() )); + + String action = platformRegisterInfo.isRegister() ? "注册" : "注销"; + logger.info(String.format("[国标级联]%s %S响应,%s ", action, response.getStatusCode(), platformRegisterInfo.getPlatformId() )); ParentPlatform parentPlatform = parentPlatformCatch.getParentPlatform(); if (parentPlatform == null) { - logger.warn(String.format("收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformGBId, action, response.getStatusCode())); + logger.warn(String.format("[国标级联]收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformRegisterInfo.getPlatformId(), action, response.getStatusCode())); return; } - if (response.getStatusCode() == 401) { + if (response.getStatusCode() == Response.UNAUTHORIZED) { WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME); - sipCommanderForPlatform.register(parentPlatform, callId, www, null, null); - }else if (response.getStatusCode() == 200){ - // 注册/注销成功 - logger.info(String.format("%s %s成功", platformGBId, action)); + try { + sipCommanderForPlatform.register(parentPlatform, callId, www, null, null, true, platformRegisterInfo.isRegister()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage()); + } + }else if (response.getStatusCode() == Response.OK){ + + if (platformRegisterInfo.isRegister()) { + platformService.online(parentPlatform); + }else { + platformService.offline(parentPlatform, false); + } + + // 注册/注销成功移除缓存的信息 redisCatchStorage.delPlatformRegisterInfo(callId); - parentPlatform.setStatus("注册".equals(action)); - // 取回Expires设置,避免注销过程中被置为0 - ParentPlatform parentPlatformTmp = storager.queryParentPlatByServerGBId(platformGBId); - String expires = parentPlatformTmp.getExpires(); - parentPlatform.setExpires(expires); - parentPlatform.setId(parentPlatformTmp.getId()); - storager.updateParentPlatformStatus(platformGBId, "注册".equals(action)); - - redisCatchStorage.updatePlatformRegister(parentPlatform); - - redisCatchStorage.updatePlatformKeepalive(parentPlatform); - - parentPlatformCatch.setParentPlatform(parentPlatform); - - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); } } 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 41650044..531505d2 100644 --- 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,8 +1,11 @@ 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.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor; +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; @@ -13,6 +16,8 @@ import javax.sip.header.CallIdHeader; @Component public class TimeoutProcessorImpl implements InitializingBean, ITimeoutProcessor { + private Logger logger = LoggerFactory.getLogger(TimeoutProcessorImpl.class); + @Autowired private SIPProcessorObserver processorObserver; @@ -26,11 +31,17 @@ public class TimeoutProcessorImpl implements InitializingBean, ITimeoutProcessor @Override public void process(TimeoutEvent event) { - // 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); + try { + // 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); + } catch (Exception e) { + logger.error("[超时事件失败]: {}", e.getMessage()); + } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/Coordtransform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/Coordtransform.java new file mode 100644 index 00000000..5c12ff6c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/Coordtransform.java @@ -0,0 +1,126 @@ +package com.genersoft.iot.vmp.gb28181.utils; + +/** + * 坐标转换 + * 一个提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的工具类 + * 参考https://github.com/wandergis/coordtransform 写的Java版本 + * @author Xinconan + * @date 2016-03-18 + * @url https://github.com/xinconan/coordtransform + */ +public class Coordtransform { + + private static double x_PI = 3.14159265358979324 * 3000.0 / 180.0; + private static double PI = 3.1415926535897932384626; + private static double a = 6378245.0; + private static double ee = 0.00669342162296594323; + + /** + * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换 + * 即 百度 转 谷歌、高德 + * @param bd_lon + * @param bd_lat + * @return Double[lon,lat] + */ + public static Double[] BD09ToGCJ02(Double bd_lon,Double bd_lat){ + double x = bd_lon - 0.0065; + double y = bd_lat - 0.006; + double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI); + double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI); + Double[] arr = new Double[2]; + arr[0] = z * Math.cos(theta); + arr[1] = z * Math.sin(theta); + return arr; + } + + /** + * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换 + * 即谷歌、高德 转 百度 + * @param gcj_lon + * @param gcj_lat + * @return Double[lon,lat] + */ + public static Double[] GCJ02ToBD09(Double gcj_lon,Double gcj_lat){ + double z = Math.sqrt(gcj_lon * gcj_lon + gcj_lat * gcj_lat) + 0.00002 * Math.sin(gcj_lat * x_PI); + double theta = Math.atan2(gcj_lat, gcj_lon) + 0.000003 * Math.cos(gcj_lon * x_PI); + Double[] arr = new Double[2]; + arr[0] = z * Math.cos(theta) + 0.0065; + arr[1] = z * Math.sin(theta) + 0.006; + return arr; + } + + /** + * WGS84转GCJ02 + * @param wgs_lon + * @param wgs_lat + * @return Double[lon,lat] + */ + public static Double[] WGS84ToGCJ02(Double wgs_lon,Double wgs_lat){ + if(outOfChina(wgs_lon, wgs_lat)){ + return new Double[]{wgs_lon,wgs_lat}; + } + double dlat = transformlat(wgs_lon - 105.0, wgs_lat - 35.0); + double dlng = transformlng(wgs_lon - 105.0, wgs_lat - 35.0); + double radlat = wgs_lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + Double[] arr = new Double[2]; + arr[0] = wgs_lon + dlng; + arr[1] = wgs_lat + dlat; + return arr; + } + + /** + * GCJ02转WGS84 + * @param gcj_lon + * @param gcj_lat + * @return Double[lon,lat] + */ + public static Double[] GCJ02ToWGS84(Double gcj_lon,Double gcj_lat){ + if(outOfChina(gcj_lon, gcj_lat)){ + return new Double[]{gcj_lon,gcj_lat}; + } + double dlat = transformlat(gcj_lon - 105.0, gcj_lat - 35.0); + double dlng = transformlng(gcj_lon - 105.0, gcj_lat - 35.0); + double radlat = gcj_lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + double mglat = gcj_lat + dlat; + double mglng = gcj_lon + dlng; + return new Double[]{gcj_lon * 2 - mglng, gcj_lat * 2 - mglat}; + } + + private static Double transformlat(double lng, double lat) { + double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0; + return ret; + } + + private static Double transformlng(double lng,double lat) { + double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0; + return ret; + } + + /** + * outOfChina + * @描述: 判断是否在国内,不在国内则不做偏移 + * @param lng + * @param lat + * @return {boolean} + */ + private static boolean outOfChina(Double lng,Double lat) { + return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false); + }; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java deleted file mode 100644 index 604c0835..00000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/DateUtil.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.utils; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -/** - * @description:时间工具类,主要处理ISO 8601格式转换 - * @author: swwheihei - * @date: 2020年5月8日 下午3:24:42 - */ -public class DateUtil { - - //private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"; - private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss"; - private static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss"; - - public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { - - SimpleDateFormat oldsdf = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()); - SimpleDateFormat newsdf = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()); - try { - return newsdf.format(oldsdf.parse(formatTime)); - } catch (ParseException e) { - e.printStackTrace(); - } - return ""; - } - - public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) { - - SimpleDateFormat oldsdf = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault()); - SimpleDateFormat newsdf = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault()); - try { - return newsdf.format(oldsdf.parse(formatTime)); - } catch (ParseException e) { - e.printStackTrace(); - } - return ""; - } - - public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) { - SimpleDateFormat format=new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss); - //设置要读取的时间字符串格式 - Date date; - try { - date = format.parse(formatTime); - Long timestamp=date.getTime()/1000; - //转换为Date类 - return timestamp; - } catch (ParseException e) { - e.printStackTrace(); - } - return 0; - } -} 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 9dd02715..f3fbbb41 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,10 +1,23 @@ package com.genersoft.iot.vmp.gb28181.utils; +import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; +import com.genersoft.iot.vmp.utils.GitUtil; 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 org.springframework.util.ObjectUtils; +import javax.sip.PeerUnavailableException; +import javax.sip.SipFactory; import javax.sip.header.FromHeader; +import javax.sip.header.Header; +import javax.sip.header.UserAgentHeader; import javax.sip.message.Request; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** * @author panlinlin @@ -18,6 +31,17 @@ public class SipUtils { FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME); return getUserIdFromFromHeader(fromHeader); } + /** + * 从subject读取channelId + * */ + public static String getChannelIdFromRequest(Request request) { + Header subject = request.getHeader("subject"); + if (subject == null) { + // 如果缺失subject + return null; + } + return ((Subject) subject).getSubject().split(":")[0]; + } public static String getUserIdFromFromHeader(FromHeader fromHeader) { AddressImpl address = (AddressImpl)fromHeader.getAddress(); @@ -25,4 +49,103 @@ public class SipUtils { return uri.getUser(); } + public static String getNewViaTag() { + return "z9hG4bK" + System.currentTimeMillis(); + } + + public static UserAgentHeader createUserAgentHeader(SipFactory sipFactory, GitUtil gitUtil) throws PeerUnavailableException, ParseException { + List agentParam = new ArrayList<>(); + agentParam.add("WVP-Pro "); + if (gitUtil != null ) { + if (!ObjectUtils.isEmpty(gitUtil.getBuildVersion())) { + agentParam.add("v"); + agentParam.add(gitUtil.getBuildVersion() + "."); + } + if (!ObjectUtils.isEmpty(gitUtil.getCommitTime())) { + agentParam.add(gitUtil.getCommitTime()); + } + } + return sipFactory.createHeaderFactory().createUserAgentHeader(agentParam); + } + + public static String getNewFromTag(){ + return UUID.randomUUID().toString().replace("-", ""); + +// return getNewTag(); + } + + public static String getNewTag(){ + return String.valueOf(System.currentTimeMillis()); + } + + + /** + * 云台指令码计算 + * + * @param leftRight 镜头左移右移 0:停止 1:左移 2:右移 + * @param upDown 镜头上移下移 0:停止 1:上移 2:下移 + * @param inOut 镜头放大缩小 0:停止 1:缩小 2:放大 + * @param moveSpeed 镜头移动速度 默认 0XFF (0-255) + * @param zoomSpeed 镜头缩放速度 默认 0X1 (0-255) + */ + public static String cmdString(int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed) { + int cmdCode = 0; + if (leftRight == 2) { + cmdCode|=0x01; // 右移 + } else if(leftRight == 1) { + cmdCode|=0x02; // 左移 + } + if (upDown == 2) { + cmdCode|=0x04; // 下移 + } else if(upDown == 1) { + cmdCode|=0x08; // 上移 + } + if (inOut == 2) { + cmdCode |= 0x10; // 放大 + } else if(inOut == 1) { + cmdCode |= 0x20; // 缩小 + } + StringBuilder builder = new StringBuilder("A50F01"); + String strTmp; + strTmp = String.format("%02X", cmdCode); + builder.append(strTmp, 0, 2); + strTmp = String.format("%02X", moveSpeed); + builder.append(strTmp, 0, 2); + builder.append(strTmp, 0, 2); + strTmp = String.format("%X", zoomSpeed); + builder.append(strTmp, 0, 1).append("0"); + //计算校验码 + int checkCode = (0XA5 + 0X0F + 0X01 + cmdCode + moveSpeed + moveSpeed + (zoomSpeed /*<< 4*/ & 0XF0)) % 0X100; + strTmp = String.format("%02X", checkCode); + builder.append(strTmp, 0, 2); + return builder.toString(); + } + + /** + * 从请求中获取设备ip地址和端口号 + * @param request 请求 + * @param sipUseSourceIpAsRemoteAddress false 从via中获取地址, true 直接获取远程地址 + * @return 地址信息 + */ + public static RemoteAddressInfo getRemoteAddressFromRequest(SIPRequest request, boolean sipUseSourceIpAsRemoteAddress) { + + String remoteAddress; + int remotePort; + if (sipUseSourceIpAsRemoteAddress) { + remoteAddress = request.getRemoteAddress().getHostAddress(); + remotePort = request.getRemotePort(); + }else { + // 判断RPort是否改变,改变则说明路由nat信息变化,修改设备信息 + // 获取到通信地址等信息 + remoteAddress = request.getTopmostViaHeader().getReceived(); + remotePort = request.getTopmostViaHeader().getRPort(); + // 解析本地地址替代 + if (ObjectUtils.isEmpty(remoteAddress) || remotePort == -1) { + remoteAddress = request.getTopmostViaHeader().getHost(); + remotePort = request.getTopmostViaHeader().getPort(); + } + } + + return new RemoteAddressInfo(remoteAddress, remotePort); + } } 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 079a78bb..35d563d9 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 @@ -1,7 +1,12 @@ package com.genersoft.iot.vmp.gb28181.utils; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.TreeType; +import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; +import com.genersoft.iot.vmp.utils.DateUtil; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; @@ -9,6 +14,7 @@ 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.StringUtils; import javax.sip.RequestEvent; @@ -19,20 +25,20 @@ import java.util.*; /** * 基于dom4j的工具包 - * - * + * + * */ public class XmlUtil { /** * 日志服务 */ - private static Logger LOG = LoggerFactory.getLogger(XmlUtil.class); + private static Logger logger = LoggerFactory.getLogger(XmlUtil.class); /** * 解析XML为Document对象 - * + * * @param xml 被解析的XMl - * + * * @return Document */ public static Element parseXml(String xml) { @@ -43,14 +49,14 @@ public class XmlUtil { try { document = saxReader.read(sr); } catch (DocumentException e) { - LOG.error("解析失败", e); + logger.error("解析失败", e); } return null == document ? null : document.getRootElement(); } /** * 获取element对象的text的值 - * + * * @param em 节点的对象 * @param tag 节点的tag * @return 节点 @@ -61,12 +67,12 @@ public class XmlUtil { } Element e = em.element(tag); // - return null == e ? null : e.getText(); + return null == e ? null : e.getText().trim(); } /** * 递归解析xml节点,适用于 多节点数据 - * + * * @param node node * @param nodeName nodeName * @return List> @@ -105,7 +111,7 @@ public class XmlUtil { /** * xml转json - * + * * @param element * @param json */ @@ -113,12 +119,12 @@ public class XmlUtil { // 如果是属性 for (Object o : element.attributes()) { Attribute attr = (Attribute) o; - if (!StringUtils.isEmpty(attr.getValue())) { + if (!ObjectUtils.isEmpty(attr.getValue())) { json.put("@" + attr.getName(), attr.getValue()); } } List chdEl = element.elements(); - if (chdEl.isEmpty() && !StringUtils.isEmpty(element.getText())) {// 如果没有子元素,只有一个值 + if (chdEl.isEmpty() && !ObjectUtils.isEmpty(element.getText())) {// 如果没有子元素,只有一个值 json.put(element.getName(), element.getText()); } @@ -149,7 +155,7 @@ public class XmlUtil { } else { // 子元素没有子元素 for (Object o : element.attributes()) { Attribute attr = (Attribute) o; - if (!StringUtils.isEmpty(attr.getValue())) { + if (!ObjectUtils.isEmpty(attr.getValue())) { json.put("@" + attr.getName(), attr.getValue()); } } @@ -178,4 +184,231 @@ public class XmlUtil { Document xml = reader.read(new ByteArrayInputStream(content)); return xml.getRootElement(); } -} + + private enum ChannelType{ + CivilCode, BusinessGroup,VirtualOrganization,Other + } + + public static DeviceChannel channelContentHander(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; + } + + ChannelType channelType = ChannelType.Other; + if (channelId.length() <= 8) { + channelType = ChannelType.CivilCode; + deviceChannel.setHasAudio(false); + }else { + if (channelId.length() == 20) { + int code = Integer.parseInt(channelId.substring(10, 13)); + switch (code){ + case 215: + channelType = ChannelType.BusinessGroup; + deviceChannel.setHasAudio(false); + break; + case 216: + channelType = ChannelType.VirtualOrganization; + deviceChannel.setHasAudio(false); + break; + case 136: + case 137: + case 138: + deviceChannel.setHasAudio(true); + break; + default: + deviceChannel.setHasAudio(false); + break; + + } + } + } + + Element channdelNameElement = itemDevice.element("Name"); + String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim() : ""; + deviceChannel.setName(channelName); + + String civilCode = XmlUtil.getText(itemDevice, "CivilCode"); + deviceChannel.setCivilCode(civilCode); + if (channelType == ChannelType.CivilCode && civilCode == null) { + deviceChannel.setParental(1); + // 行政区划如果没有传递具体值,则推测一个 + if (channelId.length() > 2) { + deviceChannel.setCivilCode(channelId.substring(0, channelId.length() - 2)); + } + } + if (channelType.equals(ChannelType.CivilCode)) { + // 行政区划其他字段没必要识别了,默认在线即可 + deviceChannel.setStatus(1); + deviceChannel.setParental(1); + deviceChannel.setCreateTime(DateUtil.getNow()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + return deviceChannel; + } + /** + * 行政区划展示设备树与业务分组展示设备树是两种不同的模式 + * 行政区划展示设备树 各个目录之间主要靠deviceId做关联,摄像头通过CivilCode指定其属于那个行政区划;都是不超过十位的编号; 结构如下: + * 河北省 + * --> 石家庄市 + * --> 摄像头 + *String parentId = XmlUtil.getText(itemDevice, "ParentID"); + if (parentId != null) { + if (parentId.contains("/")) { + String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1); + String businessGroup = parentId.substring(0, parentId.indexOf("/")); + deviceChannel.setParentId(lastParentId); + }else { + deviceChannel.setParentId(parentId); + } + } + deviceCh --> 正定县 + * --> 摄像头 + * --> 摄像头 + * + * 业务分组展示设备树是顶级是业务分组,其下的虚拟组织靠BusinessGroupID指定其所属的业务分组;摄像头通过ParentId来指定其所属于的虚拟组织: + * 业务分组 + * --> 虚拟组织 + * --> 摄像头 + * --> 虚拟组织 + * --> 摄像头 + * --> 摄像头 + */ + String parentId = XmlUtil.getText(itemDevice, "ParentID"); + String businessGroupID = XmlUtil.getText(itemDevice, "BusinessGroupID"); + if (parentId != null) { + if (parentId.contains("/")) { + String lastParentId = parentId.substring(parentId.lastIndexOf("/") + 1); + if (businessGroupID == null) { + businessGroupID = parentId.substring(0, parentId.indexOf("/")); + } + deviceChannel.setParentId(lastParentId); + }else { + deviceChannel.setParentId(parentId); + } + // 兼容设备通道信息中自己为自己父节点的情况 + if (deviceChannel.getParentId().equals(deviceChannel.getChannelId())) { + deviceChannel.setParentId(null); + } + } + deviceChannel.setBusinessGroupId(businessGroupID); + if (channelType.equals(ChannelType.BusinessGroup) || channelType.equals(ChannelType.VirtualOrganization)) { + // 业务分组和虚拟组织 其他字段没必要识别了,默认在线即可 + deviceChannel.setStatus(1); + deviceChannel.setParental(1); + deviceChannel.setCreateTime(DateUtil.getNow()); + deviceChannel.setUpdateTime(DateUtil.getNow()); + return deviceChannel; + } + + Element statusElement = itemDevice.element("Status"); + + if (statusElement != null) { + String status = statusElement.getTextTrim().trim(); + // ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理 + if (status.equals("ON") || status.equals("On") || status.equals("ONLINE") || status.equals("OK")) { + deviceChannel.setStatus(1); + } + if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) { + deviceChannel.setStatus(0); + } + }else { + deviceChannel.setStatus(1); + } + // 识别自带的目录标识 + String parental = XmlUtil.getText(itemDevice, "Parental"); + // 由于海康会错误的发送65535作为这里的取值,所以这里除非是0否则认为是1 + if (!ObjectUtils.isEmpty(parental) && parental.length() == 1 && Integer.parseInt(parental) == 0) { + deviceChannel.setParental(0); + }else { + deviceChannel.setParental(1); + } + + + deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer")); + deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model")); + deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner")); + deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum")); + deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block")); + deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address")); + deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password")); + + String safetyWay = XmlUtil.getText(itemDevice, "SafetyWay"); + if (ObjectUtils.isEmpty(safetyWay)) { + deviceChannel.setSafetyWay(0); + } else { + deviceChannel.setSafetyWay(Integer.parseInt(safetyWay)); + } + + String registerWay = XmlUtil.getText(itemDevice, "RegisterWay"); + if (ObjectUtils.isEmpty(registerWay)) { + deviceChannel.setRegisterWay(1); + } else { + deviceChannel.setRegisterWay(Integer.parseInt(registerWay)); + } + + if (XmlUtil.getText(itemDevice, "Certifiable") == null + || XmlUtil.getText(itemDevice, "Certifiable") == "") { + deviceChannel.setCertifiable(0); + } else { + deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable"))); + } + + if (XmlUtil.getText(itemDevice, "ErrCode") == null + || XmlUtil.getText(itemDevice, "ErrCode") == "") { + deviceChannel.setErrCode(0); + } else { + deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode"))); + } + + deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime")); + deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy")); + deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress")); + if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") == "") { + deviceChannel.setPort(0); + } else { + deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port"))); + } + + + String longitude = XmlUtil.getText(itemDevice, "Longitude"); + if (NumericUtil.isDouble(longitude)) { + deviceChannel.setLongitude(Double.parseDouble(longitude)); + } else { + deviceChannel.setLongitude(0.00); + } + String latitude = XmlUtil.getText(itemDevice, "Latitude"); + if (NumericUtil.isDouble(latitude)) { + deviceChannel.setLatitude(Double.parseDouble(latitude)); + } else { + deviceChannel.setLatitude(0.00); + } + deviceChannel.setGpsTime(DateUtil.getNow()); + + + if (XmlUtil.getText(itemDevice, "PTZType") == null || "".equals(XmlUtil.getText(itemDevice, "PTZType"))) { + //兼容INFO中的信息 + Element info = itemDevice.element("Info"); + if(XmlUtil.getText(info, "PTZType") == null || "".equals(XmlUtil.getText(info, "PTZType"))){ + deviceChannel.setPTZType(0); + }else{ + deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(info, "PTZType"))); + } + } else { + deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType"))); + } + return deviceChannel; + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..5ba51915 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java @@ -0,0 +1,148 @@ +package com.genersoft.iot.vmp.media.zlm; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import okhttp3.*; +import okhttp3.logging.HttpLoggingInterceptor; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.ConnectException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +@Component +public class AssistRESTfulUtils { + + private final static Logger logger = LoggerFactory.getLogger(AssistRESTfulUtils.class); + + public interface RequestCallback{ + void run(JSONObject response); + } + + private OkHttpClient getClient(){ + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); + if (logger.isDebugEnabled()) { + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> { + logger.debug("http请求参数:" + message); + }); + logging.setLevel(HttpLoggingInterceptor.Level.BASIC); + // OkHttp進行添加攔截器loggingInterceptor + httpClientBuilder.addInterceptor(logging); + } + return httpClientBuilder.build(); + } + + + public JSONObject sendGet(MediaServerItem mediaServerItem, String api, Map param, RequestCallback callback) { + OkHttpClient client = getClient(); + + if (mediaServerItem == null) { + return null; + } + if (mediaServerItem.getRecordAssistPort() <= 0) { + logger.warn("未启用Assist服务"); + return null; + } + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(String.format("http://%s:%s/%s", mediaServerItem.getIp(), mediaServerItem.getRecordAssistPort(), api)); + JSONObject responseJSON = null; + + if (param != null && param.keySet().size() > 0) { + stringBuffer.append("?"); + int index = 1; + for (String key : param.keySet()){ + if (param.get(key) != null) { + stringBuffer.append(key + "=" + param.get(key)); + if (index < param.size()) { + stringBuffer.append("&"); + } + } + index++; + } + } + + String url = stringBuffer.toString(); + Request request = new Request.Builder() + .get() + .url(url) + .build(); + if (callback == null) { + try { + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + String responseStr = responseBody.string(); + responseJSON = JSON.parseObject(responseStr); + } + }else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } catch (ConnectException e) { + logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + logger.info("请检查media配置并确认Assist已启动..."); + }catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + }else { + client.newCall(request).enqueue(new Callback(){ + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response){ + if (response.isSuccessful()) { + try { + String responseStr = Objects.requireNonNull(response.body()).string(); + callback.run(JSON.parseObject(responseStr)); + } catch (IOException e) { + logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + } + + }else { + response.close(); + Objects.requireNonNull(response.body()).close(); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + logger.error(String.format("连接Assist失败: %s, %s", e.getCause().getMessage(), e.getMessage())); + logger.info("请检查media配置并确认Assist已启动..."); + } + }); + } + + + + return responseJSON; + } + + + public JSONObject fileDuration(MediaServerItem mediaServerItem, String app, String stream, RequestCallback callback){ + Map param = new HashMap<>(); + param.put("app",app); + param.put("stream",stream); + param.put("recordIng",true); + return sendGet(mediaServerItem, "api/record/file/duration",param, callback); + } + + public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){ + Map param = new HashMap<>(); + param.put("app",app); + param.put("stream",stream); + param.put("callId",callId); + return sendGet(mediaServerItem, "api/record/addStreamCallInfo",param, callback); + } + +} 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 7eac768a..a795e77b 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -1,520 +1,683 @@ package com.genersoft.iot.vmp.media.zlm; -import java.util.List; -import java.util.UUID; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.MediaConfig; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.media.zlm.dto.*; +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.event.EventPublisher; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +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.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.media.zlm.dto.HookType; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +import com.genersoft.iot.vmp.media.zlm.dto.hook.*; import com.genersoft.iot.vmp.service.*; -import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +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.vmanager.bean.StreamContent; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.async.DeferredResult; import javax.servlet.http.HttpServletRequest; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; -/** +/** * @description:针对 ZLMediaServer的hook事件监听 * @author: swwheihei - * @date: 2020年5月8日 上午10:46:48 + * @date: 2020年5月8日 上午10:46:48 */ @RestController @RequestMapping("/index/hook") public class ZLMHttpHookListener { - private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); + private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class); - @Autowired - private SIPCommander cmder; + @Autowired + private SIPCommander cmder; - @Autowired - private IPlayService playService; + @Autowired + private SIPCommanderFroPlatform commanderFroPlatform; - @Autowired - private IVideoManagerStorager storager; + @Autowired + private IPlayService playService; - @Autowired - private IRedisCatchStorage redisCatchStorage; + @Autowired + private IVideoManagerStorage storager; - @Autowired - private IMediaServerService mediaServerService; + @Autowired + private IRedisCatchStorage redisCatchStorage; - @Autowired - private IStreamProxyService streamProxyService; + @Autowired + private IDeviceService deviceService; - @Autowired - private IStreamPushService streamPushService; + @Autowired + private IMediaServerService mediaServerService; - @Autowired - private IMediaService mediaService; + @Autowired + private IStreamProxyService streamProxyService; - @Autowired - private ZLMRESTfulUtils zlmresTfulUtils; + @Autowired + private DeferredResultHolder resultHolder; - @Autowired - private ZLMMediaListManager zlmMediaListManager; + @Autowired + private IMediaService mediaService; - @Autowired - private ZLMHttpHookSubscribe subscribe; + @Autowired + private EventPublisher eventPublisher; - @Autowired - private UserSetup userSetup; + @Autowired + private ZLMMediaListManager zlmMediaListManager; - @Autowired - private MediaConfig mediaConfig; + @Autowired + private ZlmHttpHookSubscribe subscribe; - /** - * 服务器定时上报时间,上报间隔可配置,默认10s上报一次 - * - */ - @ResponseBody - @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8") - public ResponseEntity onServerKeepalive(@RequestBody JSONObject json){ + @Autowired + private UserSetting userSetting; - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_server_keepalive API调用,参数:" + json.toString()); - } - String mediaServerId = json.getString("mediaServerId"); + @Autowired + private IUserService userService; - List subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_keepalive); - if (subscribes != null && subscribes.size() > 0) { - for (ZLMHttpHookSubscribe.Event subscribe : subscribes) { - subscribe.response(null, json); - } - } + @Autowired + private VideoStreamSessionManager sessionManager; - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } + @Autowired + private AssistRESTfulUtils assistRESTfulUtils; - /** - * 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8") - public ResponseEntity onFlowReport(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_flow_report API调用,参数:" + json.toString()); - } - String mediaServerId = json.getString("mediaServerId"); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 访问http文件服务器上hls之外的文件时触发。 - * - */ - @ResponseBody - @PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8") - public ResponseEntity onHttpAccess(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_http_access API 调用,参数:" + json.toString()); - } - String mediaServerId = json.getString("mediaServerId"); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("err", ""); - ret.put("path", ""); - ret.put("second", 600); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。 - * - */ - @ResponseBody - @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") - public ResponseEntity onPlay(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_play API调用,参数:" + json.toString()); - } - String mediaServerId = json.getString("mediaServerId"); - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_play, json); - if (subscribe != null ) { - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); - if (mediaInfo != null) { - subscribe.response(mediaInfo, json); - } + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * rtsp/rtmp/rtp推流鉴权事件。 - * - */ - @ResponseBody - @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") - public ResponseEntity onPublish(@RequestBody JSONObject json) { + /** + * 服务器定时上报时间,上报间隔可配置,默认10s上报一次 + */ + @ResponseBody + @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8") + public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) { - logger.debug("[ ZLM HOOK ]on_publish API调用,参数:" + json.toString()); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - ret.put("enableHls", true); - ret.put("enableMP4", userSetup.isRecordPushLive()); - String mediaServerId = json.getString("mediaServerId"); - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json); - if (subscribe != null) { - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); - if (mediaInfo != null) { - subscribe.response(mediaInfo, json); - }else { - ret.put("code", 1); - ret.put("msg", "zlm not register"); - } - } - String app = json.getString("app"); - String stream = json.getString("stream"); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(stream); + logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId()); - // 录像回放时不进行录像下载 - if (streamInfo != null) { - ret.put("enableMP4", false); - }else { - ret.put("enableMP4", userSetup.isRecordPushLive()); - } + taskExecutor.execute(() -> { + List subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive); + JSONObject json = (JSONObject) JSON.toJSON(param); + if (subscribes != null && subscribes.size() > 0) { + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) { + subscribe.response(null, json); + } + } + }); + mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData()); - return new ResponseEntity(ret.toString(), HttpStatus.OK); - } - - /** - * 录制mp4完成后通知事件;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8") - public ResponseEntity onRecordMp4(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_record_mp4 API调用,参数:" + json.toString()); - } - String mediaServerId = json.getString("mediaServerId"); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。 - * - */ - @ResponseBody - @PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8") - public ResponseEntity onRtspRealm(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_rtsp_realm API调用,参数:" + json.toString()); - } - String mediaServerId = json.getString("mediaServerId"); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("realm", ""); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - - /** - * 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。 - * - */ - @ResponseBody - @PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8") - public ResponseEntity onRtspAuth(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_rtsp_auth API调用,参数:" + json.toString()); - } - String mediaServerId = json.getString("mediaServerId"); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("encrypted", false); - ret.put("passwd", "test"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * shell登录鉴权,ZLMediaKit提供简单的telnet调试方式,使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面。 - * - */ - @ResponseBody - @PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8") - public ResponseEntity onShellLogin(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_shell_login API调用,参数:" + json.toString()); - } - // TODO 如果是带有rtpstream则开启按需拉流 - // String app = json.getString("app"); - // String stream = json.getString("stream"); - String mediaServerId = json.getString("mediaServerId"); - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_shell_login, json); - if (subscribe != null ) { - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); - if (mediaInfo != null) { - subscribe.response(mediaInfo, json); - } + return HookResult.SUCCESS(); + } - } + /** + * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。 + */ + @ResponseBody + @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") + public HookResult onPlay(@RequestBody OnPlayHookParam param) { + if (logger.isDebugEnabled()) { + logger.debug("[ZLM HOOK] 播放鉴权:{}->{}" + param.getMediaServerId(), param); + } + String mediaServerId = param.getMediaServerId(); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") - public ResponseEntity onStreamChanged(@RequestBody MediaItem item){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_stream_changed API调用,参数:" + JSONObject.toJSONString(item)); - } - String mediaServerId = item.getMediaServerId(); - JSONObject json = (JSONObject) JSON.toJSON(item); - ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json); - if (subscribe != null ) { - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); - if (mediaInfo != null) { - subscribe.response(mediaInfo, json); - } + taskExecutor.execute(() -> { + JSONObject json = (JSONObject) JSON.toJSON(param); + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json); + if (subscribe != null) { + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } + } + }); + if (!"rtp".equals(param.getApp())) { + Map paramMap = urlParamToMap(param.getParams()); + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream()); + if (streamAuthorityInfo != null && streamAuthorityInfo.getCallId() != null && !streamAuthorityInfo.getCallId().equals(paramMap.get("callId"))) { + return new HookResult(401, "Unauthorized"); + } + } - } - // 流消失移除redis play - String app = item.getApp(); - String streamId = item.getStream(); - String schema = item.getSchema(); - List tracks = item.getTracks(); - boolean regist = item.isRegist(); - if (tracks != null) { - logger.info("[stream: " + streamId + "] on_stream_changed->>" + schema); - } - if ("rtmp".equals(schema)){ - if (regist) { - mediaServerService.addCount(mediaServerId); - }else { - mediaServerService.removeCount(mediaServerId); - } - if ("rtp".equals(app) && !regist ) { - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); - if (streamInfo!=null){ - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - }else{ - streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); - redisCatchStorage.stopPlayback(streamInfo); - } - }else { - if (!"rtp".equals(app)){ - String type = OriginType.values()[item.getOriginType()].getType(); - MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); - if (mediaServerItem != null){ - if (regist) { - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, app, streamId, tracks); - redisCatchStorage.addStream(mediaServerItem, type, app, streamId, streamInfo); - if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal() - || item.getOriginType() == OriginType.RTMP_PUSH.ordinal() - || item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) { - zlmMediaListManager.addPush(item); - } - }else { - // 兼容流注销时类型错误的问题,等zlm更新后删除 - StreamPushItem streamPushItem = streamPushService.getPush(app, streamId); - if (streamPushItem != null) { - type = "PUSH"; - }else { - StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(app, streamId); - if (streamProxyByAppAndStream != null) { - type = "PULL"; - } - } - zlmMediaListManager.removeMedia(app, streamId); - redisCatchStorage.removeStream(mediaServerItem.getId(), type, app, streamId); - } - // 发送流变化redis消息 - JSONObject jsonObject = new JSONObject(); - jsonObject.put("serverId", userSetup.getServerId()); - jsonObject.put("app", app); - jsonObject.put("stream", streamId); - jsonObject.put("register", regist); - jsonObject.put("mediaServerId", mediaServerId); - redisCatchStorage.sendStreamChangeMsg(type, jsonObject); - } - } - } - } + return HookResult.SUCCESS(); + } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 - * - */ - @ResponseBody - @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") - public ResponseEntity onStreamNoneReader(@RequestBody JSONObject json){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_stream_none_reader API调用,参数:" + json.toString()); - } - String mediaServerId = json.getString("mediaServerId"); - String streamId = json.getString("stream"); - String app = json.getString("app"); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - if ("rtp".equals(app)){ - ret.put("close", true); - StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(streamId); - if (streamInfoForPlayCatch != null) { - // 如果在给上级推流,也不停止。 - if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) { - ret.put("close", false); - } else { - cmder.streamByeCmd(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId()); - redisCatchStorage.stopPlay(streamInfoForPlayCatch); - storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId()); - } - }else{ - StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlaybackByStreamId(streamId); - if (streamInfoForPlayBackCatch != null) { - cmder.streamByeCmd(streamInfoForPlayBackCatch.getDeviceID(), streamInfoForPlayBackCatch.getChannelId()); - redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch); - }else { - StreamInfo streamInfoForDownload = redisCatchStorage.queryDownloadByStreamId(streamId); - // 进行录像下载时无人观看不断流 - if (streamInfoForDownload != null) { - ret.put("close", false); - } - } - } - MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); - if (mediaServerItem != null && "-1".equals(mediaServerItem.getStreamNoneReaderDelayMS())) { - ret.put("close", false); - } - return new ResponseEntity(ret.toString(),HttpStatus.OK); - }else { - StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, streamId); - if (streamProxyItem != null && streamProxyItem.isEnable_remove_none_reader()) { - ret.put("close", true); - streamProxyService.del(app, streamId); - String url = streamProxyItem.getUrl() != null?streamProxyItem.getUrl():streamProxyItem.getSrc_url(); - logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", app, streamId, url); - }else { - ret.put("close", false); - } - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } + /** + * rtsp/rtmp/rtp推流鉴权事件。 + */ + @ResponseBody + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") + public HookResultForOnPublish onPublish(@RequestBody OnPublishHookParam param) { - } - - /** - * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") - public ResponseEntity onStreamNotFound(@RequestBody JSONObject json){ - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_stream_not_found API调用,参数:" + json.toString()); - } - String mediaServerId = json.getString("mediaServerId"); - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); - if (userSetup.isAutoApplyPlay() && mediaInfo != null) { - String app = json.getString("app"); - String streamId = json.getString("stream"); - if ("rtp".equals(app)) { - String[] s = streamId.split("_"); - if (s.length == 2) { - String deviceId = s[0]; - String channelId = s[1]; - Device device = redisCatchStorage.getDevice(deviceId); - if (device != null) { - UUID uuid = UUID.randomUUID(); - SSRCInfo ssrcInfo; - String streamId2 = null; - if (mediaInfo.isRtpEnable()) { - streamId2 = String.format("%s_%s", device.getDeviceId(), channelId); - } - ssrcInfo = mediaServerService.openRTPServer(mediaInfo, streamId2); - cmder.playStreamCmd(mediaInfo, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString()); - }, null); - } + JSONObject json = (JSONObject) JSON.toJSON(param); - } - } + logger.info("[ZLM HOOK]推流鉴权:{}->{}", param.getMediaServerId(), param); - } + String mediaServerId = json.getString("mediaServerId"); + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } - - /** - * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。 - * - */ - @ResponseBody - @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") - public ResponseEntity onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){ - - if (logger.isDebugEnabled()) { - logger.debug("[ ZLM HOOK ]on_server_started API调用,参数:" + jsonObject.toString()); - } - String remoteAddr = request.getRemoteAddr(); - jsonObject.put("ip", remoteAddr); - List subscribes = this.subscribe.getSubscribes(ZLMHttpHookSubscribe.HookType.on_server_started); - if (subscribes != null && subscribes.size() > 0) { - for (ZLMHttpHookSubscribe.Event subscribe : subscribes) { - subscribe.response(null, jsonObject); - } - } - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("msg", "success"); - return new ResponseEntity(ret.toString(),HttpStatus.OK); - } + if (!"rtp".equals(param.getApp())) { + if (userSetting.getPushAuthority()) { + // 推流鉴权 + if (param.getParams() == null) { + logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)"); + return new HookResultForOnPublish(401, "Unauthorized"); + } + Map paramMap = urlParamToMap(param.getParams()); + String sign = paramMap.get("sign"); + if (sign == null) { + logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)"); + return new HookResultForOnPublish(401, "Unauthorized"); + } + // 推流自定义播放鉴权码 + String callId = paramMap.get("callId"); + // 鉴权配置 + boolean hasAuthority = userService.checkPushAuthority(callId, sign); + if (!hasAuthority) { + logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign); + return new HookResultForOnPublish(401, "Unauthorized"); + } + StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param); + streamAuthorityInfo.setCallId(callId); + streamAuthorityInfo.setSign(sign); + // 鉴权通过 + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo); + // 通知assist新的callId + if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) { + taskExecutor.execute(() -> { + assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null); + }); + } + } + } else { + zlmMediaListManager.sendStreamEvent(param.getApp(), param.getStream(), param.getMediaServerId()); + } + + + HookResultForOnPublish result = HookResultForOnPublish.SUCCESS(); + if (!"rtp".equals(param.getApp())) { + result.setEnable_audio(true); + } + + taskExecutor.execute(() -> { + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json); + if (subscribe != null) { + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } else { + new HookResultForOnPublish(1, "zlm not register"); + } + } + }); + + if ("rtp".equals(param.getApp())) { + result.setEnable_mp4(userSetting.getRecordSip()); + } else { + result.setEnable_mp4(userSetting.isRecordPushLive()); + } + List ssrcTransactionForAll = sessionManager.getSsrcTransactionForAll(null, null, null, param.getStream()); + if (ssrcTransactionForAll != null && ssrcTransactionForAll.size() == 1) { + String deviceId = ssrcTransactionForAll.get(0).getDeviceId(); + String channelId = ssrcTransactionForAll.get(0).getChannelId(); + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + result.setEnable_audio(deviceChannel.isHasAudio()); + } + // 如果是录像下载就设置视频间隔十秒 + if (ssrcTransactionForAll.get(0).getType() == VideoStreamSessionManager.SessionType.download) { + result.setMp4_max_second(10); + result.setEnable_audio(true); + result.setEnable_mp4(true); + } + } + return result; + } + + /** + * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") + public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) { + + if (param.isRegist()) { + logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + } else { + logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + } + + + JSONObject json = (JSONObject) JSON.toJSON(param); + taskExecutor.execute(() -> { + ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json); + if (subscribe != null) { + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId()); + if (mediaInfo != null) { + subscribe.response(mediaInfo, json); + } + } + // 流消失移除redis play + List tracks = param.getTracks(); + if (param.isRegist()) { + if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTSP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) { + + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream()); + if (streamAuthorityInfo == null) { + streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param); + } else { + streamAuthorityInfo.setOriginType(param.getOriginType()); + streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr()); + } + redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo); + } + } else { + redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream()); + } + + if ("rtsp".equals(param.getSchema())) { + if (param.isRegist()) { + mediaServerService.addCount(param.getMediaServerId()); + } else { + mediaServerService.removeCount(param.getMediaServerId()); + } + if (param.getOriginType() == OriginType.PULL.ordinal() + || param.getOriginType() == OriginType.FFMPEG_PULL.ordinal()) { + // 设置拉流代理上线/离线 + streamProxyService.updateStatus(param.isRegist(), param.getApp(), param.getStream()); + } + if ("rtp".equals(param.getApp()) && !param.isRegist()) { + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(param.getStream()); + if (streamInfo != null) { + redisCatchStorage.stopPlay(streamInfo); + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + } else { + streamInfo = redisCatchStorage.queryPlayback(null, null, param.getStream(), null); + if (streamInfo != null) { + redisCatchStorage.stopPlayback(streamInfo.getDeviceID(), streamInfo.getChannelId(), + streamInfo.getStream(), null); + } + } + } else { + if (!"rtp".equals(param.getApp())) { + String type = OriginType.values()[param.getOriginType()].getType(); + MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); + + if (mediaServerItem != null) { + if (param.isRegist()) { + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream()); + String callId = null; + if (streamAuthorityInfo != null) { + callId = streamAuthorityInfo.getCallId(); + } + StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem, + param.getApp(), param.getStream(), tracks, callId); + param.setStreamInfo(new StreamContent(streamInfoByAppAndStream)); + redisCatchStorage.addStream(mediaServerItem, type, param.getApp(), param.getStream(), param); + if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTMP_PUSH.ordinal() + || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) { + param.setSeverId(userSetting.getServerId()); + zlmMediaListManager.addPush(param); + } + } else { + // 兼容流注销时类型从redis记录获取 + OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(param.getApp(), param.getStream(), param.getMediaServerId()); + if (onStreamChangedHookParam != null) { + type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType(); + redisCatchStorage.removeStream(mediaServerItem.getId(), type, param.getApp(), param.getStream()); + } + GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream()); + if (gbStream != null) { +// eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF); + } + zlmMediaListManager.removeMedia(param.getApp(), param.getStream()); + } + if (type != null) { + // 发送流变化redis消息 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", param.getApp()); + jsonObject.put("stream", param.getStream()); + jsonObject.put("register", param.isRegist()); + jsonObject.put("mediaServerId", param.getMediaServerId()); + redisCatchStorage.sendStreamChangeMsg(type, jsonObject); + } + } + } + } + if (!param.isRegist()) { + List sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream()); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + if (sendRtpItem.getApp().equals(param.getApp())) { + String platformId = sendRtpItem.getPlatformId(); + ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId); + Device device = deviceService.getDevice(platformId); + + try { + if (platform != null) { + commanderFroPlatform.streamByeCmd(platform, sendRtpItem); + } else { + cmder.streamByeCmd(device, sendRtpItem.getChannelId(), param.getStream(), sendRtpItem.getCallId()); + } + } catch (SipException | InvalidArgumentException | ParseException | + SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + } + } + } + } + } + }); + + return HookResult.SUCCESS(); + } + + /** + * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。 + */ + @ResponseBody + @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(), param.getApp(), param.getStream()); + JSONObject ret = new JSONObject(); + ret.put("code", 0); + // 国标类型的流 + if ("rtp".equals(param.getApp())) { + ret.put("close", userSetting.getStreamOnDemand()); + // 国标流, 点播/录像回放/录像下载 + StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(param.getStream()); + // 点播 + if (streamInfoForPlayCatch != null) { + // 收到无人观看说明流也没有在往上级推送 + if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) { + List sendRtpItems = redisCatchStorage.querySendRTPServerByChnnelId(streamInfoForPlayCatch.getChannelId()); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); + try { + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(), + sendRtpItem.getCallId(), sendRtpItem.getStreamId()); + } + } + } + Device device = deviceService.getDevice(streamInfoForPlayCatch.getDeviceID()); + if (device != null) { + try { + cmder.streamByeCmd(device, streamInfoForPlayCatch.getChannelId(), + streamInfoForPlayCatch.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[无人观看]点播, 发送BYE失败 {}", e.getMessage()); + } + } + + redisCatchStorage.stopPlay(streamInfoForPlayCatch); + storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId()); + return ret; + } + // 录像回放 + StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null); + if (streamInfoForPlayBackCatch != null) { + if (streamInfoForPlayBackCatch.isPause()) { + ret.put("close", false); + } else { + Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID()); + if (device != null) { + try { + cmder.streamByeCmd(device, streamInfoForPlayBackCatch.getChannelId(), + streamInfoForPlayBackCatch.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | + SsrcTransactionNotFoundException e) { + logger.error("[无人观看]回放, 发送BYE失败 {}", e.getMessage()); + } + } + redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch.getDeviceID(), + streamInfoForPlayBackCatch.getChannelId(), streamInfoForPlayBackCatch.getStream(), null); + } + return ret; + } + // 录像下载 + StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null); + // 进行录像下载时无人观看不断流 + if (streamInfoForDownload != null) { + ret.put("close", false); + return ret; + } + } else { + // 非国标流 推流/拉流代理 + // 拉流代理 + StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); + if (streamProxyItem != null) { + if (streamProxyItem.isEnable_remove_none_reader()) { + // 无人观看自动移除 + ret.put("close", true); + streamProxyService.del(param.getApp(), param.getStream()); + String url = streamProxyItem.getUrl() != null ? streamProxyItem.getUrl() : streamProxyItem.getSrc_url(); + logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除", param.getApp(), param.getStream(), url); + } else if (streamProxyItem.isEnable_disable_none_reader()) { + // 无人观看停用 + ret.put("close", true); + // 修改数据 + streamProxyService.stop(param.getApp(), param.getStream()); + } else { + // 无人观看不做处理 + ret.put("close", false); + } + return ret; + } + // 推流具有主动性,暂时不做处理 +// StreamPushItem streamPushItem = streamPushService.getPush(app, streamId); +// if (streamPushItem != null) { +// // TODO 发送停止 +// +// } + } + return ret; + } + + /** + * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8") + public DeferredResult onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param) { + logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + + DeferredResult defaultResult = new DeferredResult<>(); + + MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId()); + if (!userSetting.isAutoApplyPlay() || mediaInfo == null) { + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); + return defaultResult; + } + + if ("rtp".equals(param.getApp())) { + String[] s = param.getStream().split("_"); + if (!mediaInfo.isRtpEnable() || s.length != 2) { + defaultResult.setResult(HookResult.SUCCESS()); + return defaultResult; + } + String deviceId = s[0]; + String channelId = s[1]; + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null) { + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); + return defaultResult; + } + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel == null) { + defaultResult.setResult(new HookResult(ErrorCode.ERROR404.getCode(), ErrorCode.ERROR404.getMsg())); + return defaultResult; + } + logger.info("[ZLM HOOK] 流未找到, 发起自动点播:{}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream()); + RequestMessage msg = new RequestMessage(); + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; + boolean exist = resultHolder.exist(key, null); + msg.setKey(key); + String uuid = UUID.randomUUID().toString(); + msg.setId(uuid); + DeferredResult result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + DeferredResultEx deferredResultEx = new DeferredResultEx<>(result); + + result.onTimeout(() -> { + logger.info("点播接口等待超时"); + // 释放rtpserver + msg.setData(new HookResult(ErrorCode.ERROR100.getCode(), "点播超时")); + resultHolder.invokeResult(msg); + }); + // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误 + deferredResultEx.setFilter(result1 -> { + WVPResult wvpResult1 = (WVPResult) result1; + HookResult resultForEnd = new HookResult(); + resultForEnd.setCode(wvpResult1.getCode()); + resultForEnd.setMsg(wvpResult1.getMsg()); + return resultForEnd; + }); + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, deferredResultEx); + + if (!exist) { + playService.play(mediaInfo, deviceId, channelId, null, eventResult -> { + msg.setData(new HookResult(eventResult.statusCode, eventResult.msg)); + resultHolder.invokeResult(msg); + }, null); + } + return result; + } else { + // 拉流代理 + StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream()); + if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) { + streamProxyService.start(param.getApp(), param.getStream()); + } + DeferredResult result = new DeferredResult<>(); + result.setResult(HookResult.SUCCESS()); + return result; + } + } + + /** + * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。 + */ + @ResponseBody + @PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8") + public HookResult onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject) { + + jsonObject.put("ip", request.getRemoteAddr()); + ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject); + zlmServerConfig.setIp(request.getRemoteAddr()); + logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId()); + taskExecutor.execute(() -> { + List subscribes = this.subscribe.getSubscribes(HookType.on_server_started); + if (subscribes != null && subscribes.size() > 0) { + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) { + subscribe.response(null, jsonObject); + } + } + mediaServerService.zlmServerOnline(zlmServerConfig); + }); + + return HookResult.SUCCESS(); + } + + /** + * 发送rtp(startSendRtp)被动关闭时回调 + */ + @ResponseBody + @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()); + + // 查找对应的上级推流,发送停止 + if (!"rtp".equals(param.getApp())) { + return HookResult.SUCCESS(); + } + taskExecutor.execute(() -> { + List sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream()); + if (sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId()); + try { + commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId()); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + } + redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(), + sendRtpItem.getCallId(), sendRtpItem.getStreamId()); + } + } + }); + + return HookResult.SUCCESS(); + } + + /** + * rtpServer收流超时 + */ + @ResponseBody + @PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8") + public HookResult onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param) { + logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc()); + + taskExecutor.execute(() -> { + JSONObject json = (JSONObject) JSON.toJSON(param); + List subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout); + if (subscribes != null && subscribes.size() > 0) { + for (ZlmHttpHookSubscribe.Event subscribe : subscribes) { + subscribe.response(null, json); + } + } + }); + + return HookResult.SUCCESS(); + } + + private Map urlParamToMap(String params) { + HashMap map = new HashMap<>(); + if (ObjectUtils.isEmpty(params)) { + return map; + } + String[] paramsArray = params.split("&"); + if (paramsArray.length == 0) { + return map; + } + for (String param : paramsArray) { + String[] paramArray = param.split("="); + if (paramArray.length == 2) { + map.put(paramArray[0], paramArray[1]); + } + } + return map; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java deleted file mode 100644 index c8cca53e..00000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.genersoft.iot.vmp.media.zlm; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; -import org.springframework.stereotype.Component; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @description:针对 ZLMediaServer的hook事件订阅 - * @author: pan - * @date: 2020年12月2日 21:17:32 - */ -@Component -public class ZLMHttpHookSubscribe { - - public enum HookType{ - on_flow_report, - on_http_access, - on_play, - on_publish, - on_record_mp4, - on_rtsp_auth, - on_rtsp_realm, - on_shell_login, - on_stream_changed, - on_stream_none_reader, - on_stream_not_found, - on_server_started, - on_server_keepalive - } - - public interface Event{ - void response(MediaServerItem mediaServerItem, JSONObject response); - } - - private Map> allSubscribes = new ConcurrentHashMap<>(); - - public void addSubscribe(HookType type, JSONObject hookResponse, ZLMHttpHookSubscribe.Event event) { - Map eventMap = allSubscribes.get(type); - if (eventMap == null) { - eventMap = new HashMap(); - allSubscribes.put(type,eventMap); - } - eventMap.put(hookResponse, event); - } - - public ZLMHttpHookSubscribe.Event getSubscribe(HookType type, JSONObject hookResponse) { - ZLMHttpHookSubscribe.Event event= null; - Map eventMap = allSubscribes.get(type); - if (eventMap == null) { - return null; - } - for (JSONObject key : eventMap.keySet()) { - Boolean result = null; - for (String s : key.keySet()) { - if (result == null) { - result = key.getString(s).equals(hookResponse.getString(s)); - }else { - if (key.getString(s) == null) { - continue; - } - result = result && key.getString(s).equals(hookResponse.getString(s)); - } - - } - if (null != result && result) { - event = eventMap.get(key); - } - } - return event; - } - - public void removeSubscribe(HookType type, JSONObject hookResponse) { - Map eventMap = allSubscribes.get(type); - if (eventMap == null) { - return; - } - Iterator> iterator = eventMap.entrySet().iterator(); - while (iterator.hasNext()){ - Map.Entry next = iterator.next(); - JSONObject key = next.getKey(); - Boolean result = null; - for (String s : key.keySet()) { - if (result == null) { - result = key.getString(s).equals(hookResponse.getString(s)); - }else { - if (key.getString(s) == null) continue; - result = result && key.getString(s).equals(hookResponse.getString(s)); - } - } - if (null != result && result){ - iterator.remove(); - } - } - } - - /** - * 获取某个类型的所有的订阅 - * @param type - * @return - */ - public List getSubscribes(HookType type) { - // ZLMHttpHookSubscribe.Event event= null; - Map eventMap = allSubscribes.get(type); - if (eventMap == null) { - return null; - } - List result = new ArrayList<>(); - for (JSONObject key : eventMap.keySet()) { - result.add(eventMap.get(key)); - } - return result; - } - - -} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java index 5b7ba1cc..db2beb0c 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java @@ -1,30 +1,30 @@ package com.genersoft.iot.vmp.media.zlm; -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.conf.UserSetup; +import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; -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.*; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; +import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.service.IStreamProxyService; import com.genersoft.iot.vmp.service.IStreamPushService; -import com.genersoft.iot.vmp.service.bean.ThirdPartyGB; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +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.StreamPushMapper; +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.stereotype.Component; -import org.springframework.util.StringUtils; +import java.text.ParseException; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.concurrent.ConcurrentHashMap; +/** + * @author lin + */ @Component public class ZLMMediaListManager { @@ -37,7 +37,7 @@ public class ZLMMediaListManager { private IRedisCatchStorage redisCatchStorage; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private GbStreamMapper gbStreamMapper; @@ -55,161 +55,83 @@ public class ZLMMediaListManager { private StreamPushMapper streamPushMapper; @Autowired - private ZLMHttpHookSubscribe subscribe; + private ZlmHttpHookSubscribe subscribe; @Autowired - private UserSetup userSetup; + private UserSetting userSetting; + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; - public void updateMediaList(MediaServerItem mediaServerItem) { - storager.clearMediaList(); + @Autowired + private IMediaServerService mediaServerService; - // 使用异步的当时更新媒体流列表 - zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{ - if (mediaList == null) return; - String dataStr = mediaList.getString("data"); + private Map channelOnPublishEvents = new ConcurrentHashMap<>(); - Integer code = mediaList.getInteger("code"); - Map result = new HashMap<>(); - List streamPushItems = null; - // 获取所有的国标关联 -// List gbStreams = gbStreamMapper.selectAllByMediaServerId(mediaServerItem.getId()); - if (code == 0 ) { - if (dataStr != null) { - streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem); - } - }else { - logger.warn("更新视频流失败,错误code: " + code); + public StreamPushItem addPush(OnStreamChangedHookParam onStreamChangedHookParam) { + StreamPushItem transform = streamPushService.transform(onStreamChangedHookParam); + StreamPushItem pushInDb = streamPushService.getPush(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream()); + transform.setPushIng(onStreamChangedHookParam.isRegist()); + transform.setUpdateTime(DateUtil.getNow()); + transform.setPushTime(DateUtil.getNow()); + transform.setSelf(userSetting.getServerId().equals(onStreamChangedHookParam.getSeverId())); + if (pushInDb == null) { + transform.setCreateTime(DateUtil.getNow()); + streamPushMapper.add(transform); + }else { + streamPushMapper.update(transform); + gbStreamMapper.updateMediaServer(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream(), onStreamChangedHookParam.getMediaServerId()); + } + ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(transform.getApp(), transform.getStream()); + if ( channelOnlineEventLister != null) { + try { + channelOnlineEventLister.run(transform.getApp(), transform.getStream(), transform.getServerId());; + } catch (ParseException e) { + logger.error("addPush: ", e); } - - if (streamPushItems != null) { - storager.updateMediaList(streamPushItems); - for (StreamPushItem streamPushItem : streamPushItems) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("app", streamPushItem.getApp()); - jsonObject.put("stream", streamPushItem.getStream()); - jsonObject.put("mediaServerId", mediaServerItem.getId()); - subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject, - (MediaServerItem mediaServerItemInuse, JSONObject response)->{ - updateMedia(mediaServerItem, response.getString("app"), response.getString("stream")); - } - ); - } - } - })); - + removedChannelOnlineEventLister(transform.getApp(), transform.getStream()); + } + return transform; } - public void addMedia(MediaServerItem mediaServerItem, String app, String streamId) { - //使用异步更新推流 - updateMedia(mediaServerItem, app, streamId); - } - - public void addPush(MediaItem mediaItem) { - // 查找此直播流是否存在redis预设gbId - StreamPushItem transform = streamPushService.transform(mediaItem); - // 从streamId取出查询关键值 - Pattern pattern = Pattern.compile(userSetup.getThirdPartyGBIdReg()); - Matcher matcher = pattern.matcher(mediaItem.getStream());// 指定要匹配的字符串 - String queryKey = null; - if (matcher.find()) { //此处find()每次被调用后,会偏移到下一个匹配 - queryKey = matcher.group(); - } - if (queryKey != null) { - ThirdPartyGB thirdPartyGB = redisCatchStorage.queryMemberNoGBId(queryKey); - if (thirdPartyGB != null && !StringUtils.isEmpty(thirdPartyGB.getNationalStandardNo())) { - transform.setGbId(thirdPartyGB.getNationalStandardNo()); - transform.setName(thirdPartyGB.getName()); - } - } - storager.updateMedia(transform); - if (!StringUtils.isEmpty(transform.getGbId())) { - // 如果这个国标ID已经给了其他推流且流已离线,则移除其他推流 - List gbStreams = gbStreamMapper.selectByGBId(transform.getGbId()); - if (gbStreams.size() > 0) { - for (GbStream gbStream : gbStreams) { - // 出现使用相同国标Id的视频流时,使用新流替换旧流, - gbStreamMapper.del(gbStream.getApp(), gbStream.getStream()); - platformGbStreamMapper.delByAppAndStream(gbStream.getApp(), gbStream.getStream()); - if (!gbStream.isStatus()) { - streamPushMapper.del(gbStream.getApp(), gbStream.getStream()); - } + public void sendStreamEvent(String app, String stream, String mediaServerId) { + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + // 查看推流状态 + if (zlmrtpServerFactory.isStreamReady(mediaServerItem, app, stream)) { + ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(app, stream); + if (channelOnlineEventLister != null) { + try { + channelOnlineEventLister.run(app, stream, mediaServerId); + } catch (ParseException e) { + logger.error("sendStreamEvent: ", e); } - } - if (gbStreamMapper.selectOne(transform.getApp(), transform.getStream()) != null) { - gbStreamMapper.update(transform); - }else { - gbStreamMapper.add(transform); + removedChannelOnlineEventLister(app, stream); } } } - - public void updateMedia(MediaServerItem mediaServerItem, String app, String streamId) { - //使用异步更新推流 - zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId, "rtmp", json->{ - - if (json == null) return; - String dataStr = json.getString("data"); - - Integer code = json.getInteger("code"); - Map result = new HashMap<>(); - List streamPushItems = null; - if (code == 0 ) { - if (dataStr != null) { - streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem); - } - }else { - logger.warn("更新视频流失败,错误code: " + code); - } - - if (streamPushItems != null && streamPushItems.size() == 1) { - storager.updateMedia(streamPushItems.get(0)); - } - }); - } - - public int removeMedia(String app, String streamId) { // 查找是否关联了国标, 关联了不删除, 置为离线 - StreamProxyItem streamProxyItem = gbStreamMapper.selectOne(app, streamId); - int result = 0; - if (streamProxyItem == null) { + GbStream gbStream = gbStreamMapper.selectOne(app, streamId); + int result; + if (gbStream == null) { result = storager.removeMedia(app, streamId); }else { - result =storager.mediaOutline(app, streamId); + result =storager.mediaOffline(app, streamId); } return result; } + public void addChannelOnlineEventLister(String app, String stream, ChannelOnlineEvent callback) { + this.channelOnPublishEvents.put(app + "_" + stream, callback); + } + public void removedChannelOnlineEventLister(String app, String stream) { + this.channelOnPublishEvents.remove(app + "_" + stream); + } + + public ChannelOnlineEvent getChannelOnlineEventLister(String app, String stream) { + return this.channelOnPublishEvents.get(app + "_" + stream); + } -// public void clearAllSessions() { -// logger.info("清空所有国标相关的session"); -// JSONObject allSessionJSON = zlmresTfulUtils.getAllSession(); -// ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo(); -// HashSet allLocalPorts = new HashSet(); -// if (allSessionJSON.getInteger("code") == 0) { -// JSONArray data = allSessionJSON.getJSONArray("data"); -// if (data.size() > 0) { -// for (int i = 0; i < data.size(); i++) { -// JSONObject sessionJOSN = data.getJSONObject(i); -// Integer local_port = sessionJOSN.getInteger("local_port"); -// if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) && -// !local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){ -// allLocalPorts.add(sessionJOSN.getInteger("local_port") + ""); -// } -// } -// } -// } -// if (allLocalPorts.size() > 0) { -// List result = new ArrayList<>(allLocalPorts); -// String localPortSStr = String.join(",", result); -// zlmresTfulUtils.kickSessions(localPortSStr); -// } -// } } 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 e4bcd31a..99a695eb 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java @@ -1,9 +1,10 @@ package com.genersoft.iot.vmp.media.zlm; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import okhttp3.*; +import okhttp3.logging.HttpLoggingInterceptor; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,6 +12,7 @@ import org.springframework.stereotype.Component; import java.io.*; import java.net.ConnectException; +import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -21,14 +23,48 @@ public class ZLMRESTfulUtils { private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class); + private OkHttpClient client; + public interface RequestCallback{ void run(JSONObject response); } + private OkHttpClient getClient(){ + if (client == null) { + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); + //todo 暂时写死超时时间 均为5s + // 设置连接超时时间 + httpClientBuilder.connectTimeout(5,TimeUnit.SECONDS); + // 设置读取超时时间 + httpClientBuilder.readTimeout(5,TimeUnit.SECONDS); + // 设置连接池 + httpClientBuilder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES)); + if (logger.isDebugEnabled()) { + HttpLoggingInterceptor logging = new HttpLoggingInterceptor(message -> { + logger.debug("http请求参数:" + message); + }); + logging.setLevel(HttpLoggingInterceptor.Level.BASIC); + // OkHttp進行添加攔截器loggingInterceptor + httpClientBuilder.addInterceptor(logging); + } + client = httpClientBuilder.build(); + } + return client; + + } + + public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map param, RequestCallback callback) { - OkHttpClient client = new OkHttpClient(); + OkHttpClient client = getClient(); + + if (mediaServerItem == null) { + return null; + } String url = String.format("http://%s:%s/index/api/%s", mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api); - JSONObject responseJSON = null; + JSONObject responseJSON = new JSONObject(); + //-2自定义流媒体 调用错误码 + responseJSON.put("code",-2); + responseJSON.put("msg","流媒体调用失败"); FormBody.Builder builder = new FormBody.Builder(); builder.add("secret",mediaServerItem.getSecret()); @@ -59,11 +95,20 @@ public class ZLMRESTfulUtils { response.close(); Objects.requireNonNull(response.body()).close(); } - } catch (ConnectException e) { - logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage())); - logger.info("请检查media配置并确认ZLM已启动..."); }catch (IOException e) { logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage())); + + if(e instanceof SocketTimeoutException){ + //读取超时超时异常 + logger.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())); + } + + }catch (Exception e){ + logger.error(String.format("访问ZLM失败: %s, %s", url, e.getMessage())); } }else { client.newCall(request).enqueue(new Callback(){ @@ -86,8 +131,16 @@ public class ZLMRESTfulUtils { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { - logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage())); - logger.info("请检查media配置并确认ZLM已启动..."); + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); + + if(e instanceof SocketTimeoutException){ + //读取超时超时异常 + logger.error(String.format("读取ZLM数据失败: %s, %s", call.request().toString(), e.getMessage())); + } + if(e instanceof ConnectException){ + //判断连接异常,我这里是报Failed to connect to 10.7.5.144 + logger.error(String.format("连接ZLM失败: %s, %s", call.request().toString(), e.getMessage())); + } } }); } @@ -118,12 +171,9 @@ public class ZLMRESTfulUtils { .build(); logger.info(request.toString()); try { - OkHttpClient client = new OkHttpClient.Builder() - .readTimeout(10, TimeUnit.SECONDS) - .build(); + OkHttpClient client = getClient(); Response response = client.newCall(request).execute(); if (response.isSuccessful()) { - logger.info("response body contentType: " + Objects.requireNonNull(response.body()).contentType()); if (targetPath != null) { File snapFolder = new File(targetPath); if (!snapFolder.exists()) { @@ -132,7 +182,7 @@ public class ZLMRESTfulUtils { } } - File snapFile = new File(targetPath + "/" + fileName); + File snapFile = new File(targetPath + File.separator + fileName); FileOutputStream outStream = new FileOutputStream(snapFile); outStream.write(Objects.requireNonNull(response.body()).bytes()); @@ -191,14 +241,13 @@ public class ZLMRESTfulUtils { } public JSONObject addFFmpegSource(MediaServerItem mediaServerItem, String src_url, String dst_url, String timeout_ms, - boolean enable_hls, boolean enable_mp4, String ffmpeg_cmd_key){ + boolean enable_audio, boolean enable_mp4, String ffmpeg_cmd_key){ logger.info(src_url); logger.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("enable_hls", enable_hls); param.put("enable_mp4", enable_mp4); param.put("ffmpeg_cmd_key", ffmpeg_cmd_key); return sendPost(mediaServerItem, "addFFmpegSource",param, null); @@ -238,14 +287,18 @@ public class ZLMRESTfulUtils { return sendPost(mediaServerItem, "stopSendRtp",param, null); } - public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) { + public JSONObject restartServer(MediaServerItem mediaServerItem) { + return sendPost(mediaServerItem, "restartServer",null, null); + } + + public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_audio, boolean enable_mp4, String rtp_type) { Map param = new HashMap<>(); param.put("vhost", "__defaultVhost__"); param.put("app", app); param.put("stream", stream); param.put("url", url); - param.put("enable_hls", enable_hls?1:0); param.put("enable_mp4", enable_mp4?1:0); + param.put("enable_audio", enable_audio?1:0); param.put("rtp_type", rtp_type); return sendPost(mediaServerItem, "addStreamProxy",param, null); } @@ -270,10 +323,22 @@ public class ZLMRESTfulUtils { } public void getSnap(MediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) { - Map param = new HashMap<>(); + Map param = new HashMap<>(3); param.put("url", flvUrl); param.put("timeout_sec", timeout_sec); param.put("expire_sec", expire_sec); sendGetForImg(mediaServerItem, "getSnap", param, targetPath, fileName); } + + public JSONObject pauseRtpCheck(MediaServerItem mediaServerItem, String streamId) { + Map param = new HashMap<>(1); + param.put("stream_id", streamId); + return sendPost(mediaServerItem, "pauseRtpCheck",param, null); + } + + public JSONObject resumeRtpCheck(MediaServerItem mediaServerItem, String streamId) { + Map param = new HashMap<>(1); + param.put("stream_id", streamId); + return sendPost(mediaServerItem, "resumeRtpCheck",param, null); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java index 30a15096..c7105698 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java @@ -1,17 +1,17 @@ package com.genersoft.iot.vmp.media.zlm; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import java.util.HashMap; -import java.util.Map; +import java.util.*; @Component public class ZLMRTPServerFactory { @@ -21,60 +21,67 @@ public class ZLMRTPServerFactory { @Autowired private ZLMRESTfulUtils zlmresTfulUtils; + @Autowired + private UserSetting userSetting; + + @Autowired + private ZlmHttpHookSubscribe hookSubscribe; + private int[] portRangeArray = new int[2]; - public int createRTPServer(MediaServerItem mediaServerItem, String streamId) { - Map currentStreams = new HashMap<>(); + public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List usedFreelist) { + if (endPort <= startPort) { + return -1; + } + if (usedFreelist == null) { + usedFreelist = new ArrayList<>(); + } JSONObject listRtpServerJsonResult = zlmresTfulUtils.listRtpServer(mediaServerItem); if (listRtpServerJsonResult != null) { JSONArray data = listRtpServerJsonResult.getJSONArray("data"); if (data != null) { for (int i = 0; i < data.size(); i++) { JSONObject dataItem = data.getJSONObject(i); - currentStreams.put(dataItem.getString("stream_id"), dataItem.getInteger("port")); + usedFreelist.add(dataItem.getInteger("port")); } } } - // 已经在推流 - if (currentStreams.get(streamId) != null) { - Map closeRtpServerParam = new HashMap<>(); - closeRtpServerParam.put("stream_id", streamId); - zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam); - currentStreams.remove(streamId); - } Map param = new HashMap<>(); int result = -1; - /** - * 不设置推流端口端则使用随机端口 - */ - if (StringUtils.isEmpty(mediaServerItem.getSendRtpPortRange())){ - param.put("port", 0); - }else { - int newPort = getPortFromportRange(mediaServerItem); - param.put("port", newPort); + // 设置推流端口 + if (startPort%2 == 1) { + startPort ++; } + boolean checkPort = false; + for (int i = startPort; i < endPort + 1; i+=2) { + if (!usedFreelist.contains(i)){ + checkPort = true; + startPort = i; + break; + } + } + if (!checkPort) { + logger.warn("未找到节点{}上范围[{}-{}]的空闲端口", mediaServerItem.getId(), startPort, endPort); + return -1; + } + param.put("port", startPort); + String stream = UUID.randomUUID().toString(); param.put("enable_tcp", 1); - param.put("stream_id", streamId); + param.put("stream_id", stream); +// param.put("port", 0); JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param); if (openRtpServerResultJson != null) { - switch (openRtpServerResultJson.getInteger("code")){ - case 0: - result= openRtpServerResultJson.getInteger("port"); - break; - case -300: // id已经存在, 可能已经在其他端口推流 - Map closeRtpServerParam = new HashMap<>(); - closeRtpServerParam.put("stream_id", streamId); - zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam); - result = createRTPServer(mediaServerItem, streamId);; - break; - case -400: // 端口占用 - result= createRTPServer(mediaServerItem, streamId); - break; - default: - logger.error("创建RTP Server 失败 {}: " + openRtpServerResultJson.getString("msg"), param.get("port")); - break; + if (openRtpServerResultJson.getInteger("code") == 0) { + result= openRtpServerResultJson.getInteger("port"); + Map closeRtpServerParam = new HashMap<>(); + closeRtpServerParam.put("stream_id", stream); + zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam); + }else { + usedFreelist.add(startPort); + startPort +=2; + result = getFreePort(mediaServerItem, startPort, endPort,usedFreelist); } }else { // 检查ZLM状态 @@ -83,7 +90,61 @@ public class ZLMRTPServerFactory { return result; } - public boolean closeRTPServer(MediaServerItem serverItem, String streamId) { + public int createRTPServer(MediaServerItem mediaServerItem, String streamId, int ssrc, Integer port) { + 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"); + if (result == 0) { + // 此时说明rtpServer已经创建但是流还没有推上来 + // 此时重新打开rtpServer + Map param = new HashMap<>(); + param.put("stream_id", streamId); + JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(mediaServerItem, param); + if (jsonObject != null ) { + if (jsonObject.getInteger("code") == 0) { + return createRTPServer(mediaServerItem, streamId, ssrc, port); + }else { + logger.warn("[开启rtpServer], 重启RtpServer错误"); + } + } + } + return result; + } + }else if(rtpInfo.getInteger("code") == -2){ + return result; + } + + Map param = new HashMap<>(); + + param.put("enable_tcp", 1); + param.put("stream_id", streamId); + // 推流端口设置0则使用随机端口 + if (port == null) { + param.put("port", 0); + }else { + param.put("port", port); + } + param.put("ssrc", ssrc); + 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")); + } + }else { + // 检查ZLM状态 + logger.error("创建RTP Server 失败 {}: 请检查ZLM服务", param.get("port")); + } + return result; + } + + public boolean closeRtpServer(MediaServerItem serverItem, String streamId) { boolean result = false; if (serverItem !=null){ Map param = new HashMap<>(); @@ -103,32 +164,6 @@ public class ZLMRTPServerFactory { return result; } - private int getPortFromportRange(MediaServerItem mediaServerItem) { - int currentPort = mediaServerItem.getCurrentPort(); - if (currentPort == 0) { - String[] portRangeStrArray = mediaServerItem.getSendRtpPortRange().split(","); - if (portRangeStrArray.length != 2) { - portRangeArray[0] = 30000; - portRangeArray[1] = 30500; - }else { - portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]); - portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]); - } - } - - if (currentPort == 0 || currentPort++ > portRangeArray[1]) { - currentPort = portRangeArray[0]; - mediaServerItem.setCurrentPort(currentPort); - return portRangeArray[0]; - } else { - if (currentPort % 2 == 1) { - currentPort++; - } - currentPort++; - mediaServerItem.setCurrentPort(currentPort); - return currentPort; - } - } /** * 创建一个国标推流 @@ -140,18 +175,17 @@ public class ZLMRTPServerFactory { * @param tcp 是否为tcp * @return SendRtpItem */ - public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){ + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp, boolean rtcp){ - // 使用RTPServer 功能找一个可用的端口 - String playSsrc = serverItem.getSsrcConfig().getPlaySsrc(); - int localPort = createRTPServer(serverItem, playSsrc); - if (localPort != -1) { - // TODO 高并发时可能因为未放入缓存而ssrc冲突 - serverItem.getSsrcConfig().releaseSsrc(playSsrc); - closeRTPServer(serverItem, playSsrc); - }else { - logger.error("没有可用的端口"); - return null; + // 默认为随机端口 + int localPort = 0; + if (userSetting.getGbSendStreamStrict()) { + if (userSetting.getGbSendStreamStrict()) { + localPort = keepPort(serverItem, ssrc); + if (localPort == 0) { + return null; + } + } } SendRtpItem sendRtpItem = new SendRtpItem(); sendRtpItem.setIp(ip); @@ -161,8 +195,10 @@ public class ZLMRTPServerFactory { sendRtpItem.setDeviceId(deviceId); sendRtpItem.setChannelId(channelId); sendRtpItem.setTcp(tcp); + sendRtpItem.setRtcp(rtcp); sendRtpItem.setApp("rtp"); sendRtpItem.setLocalPort(localPort); + sendRtpItem.setServerId(userSetting.getServerId()); sendRtpItem.setMediaServerId(serverItem.getId()); return sendRtpItem; } @@ -177,16 +213,14 @@ public class ZLMRTPServerFactory { * @param tcp 是否为tcp * @return SendRtpItem */ - public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){ - String playSsrc = serverItem.getSsrcConfig().getPlaySsrc(); - int localPort = createRTPServer(serverItem, playSsrc); - if (localPort != -1) { - // TODO 高并发时可能因为未放入缓存而ssrc冲突 - serverItem.getSsrcConfig().releaseSsrc(ssrc); - closeRTPServer(serverItem, playSsrc); - }else { - logger.error("没有可用的端口"); - return null; + public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp, boolean rtcp){ + // 默认为随机端口 + int localPort = 0; + if (userSetting.getGbSendStreamStrict()) { + localPort = keepPort(serverItem, ssrc); + if (localPort == 0) { + return null; + } } SendRtpItem sendRtpItem = new SendRtpItem(); sendRtpItem.setIp(ip); @@ -198,32 +232,60 @@ public class ZLMRTPServerFactory { sendRtpItem.setChannelId(channelId); sendRtpItem.setTcp(tcp); sendRtpItem.setLocalPort(localPort); + sendRtpItem.setServerId(userSetting.getServerId()); sendRtpItem.setMediaServerId(serverItem.getId()); + sendRtpItem.setRtcp(rtcp); return sendRtpItem; } /** - * 调用zlm RESTful API —— startSendRtp + * 保持端口,直到需要需要发流时再释放 */ - public Boolean startSendRtpStream(MediaServerItem mediaServerItem, Mapparam) { - Boolean result = false; - JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServerItem, param); - if (jsonObject == null) { - logger.error("RTP推流失败: 请检查ZLM服务"); - } else if (jsonObject.getInteger("code") == 0) { - result= true; - logger.info("RTP推流[ {}/{} ]请求成功,本地推流端口:{}" ,param.get("app"), param.get("stream"), jsonObject.getString("local_port")); - } else { - logger.error("RTP推流失败: " + jsonObject.getString("msg")); + public int keepPort(MediaServerItem serverItem, String ssrc) { + int localPort = 0; + Map param = new HashMap<>(3); + param.put("port", 0); + param.put("enable_tcp", 1); + param.put("stream_id", ssrc); + JSONObject jsonObject = zlmresTfulUtils.openRtpServer(serverItem, param); + if (jsonObject.getInteger("code") == 0) { + localPort = jsonObject.getInteger("port"); + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId()); + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 + hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout, + (MediaServerItem mediaServerItem, JSONObject response)->{ + logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc); + keepPort(serverItem, ssrc); + }); } - return result; + logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort); + return localPort; + } + + /** + * 释放保持的端口 + */ + public boolean releasePort(MediaServerItem serverItem, String ssrc) { + logger.info("[上级点播] {}->释放监听端口", ssrc); + boolean closeRTPServerResult = closeRtpServer(serverItem, ssrc); + HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId()); + // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 + hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout); + return closeRTPServerResult; + } + + /** + * 调用zlm RESTFUL API —— startSendRtp + */ + public JSONObject startSendRtpStream(MediaServerItem mediaServerItem, Mapparam) { + return zlmresTfulUtils.startSendRtp(mediaServerItem, param); } /** * 查询待转推的流是否就绪 */ public Boolean isRtpReady(MediaServerItem mediaServerItem, String streamId) { - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem,"rtp", "rtmp", streamId); + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem,"rtp", "rtsp", streamId); return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); } @@ -231,8 +293,11 @@ public class ZLMRTPServerFactory { * 查询待转推的流是否就绪 */ public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) { - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId); - return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online")); + JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId); + return mediaInfo != null && (mediaInfo.getInteger("code") == 0 + + && mediaInfo.getJSONArray("data") != null + && mediaInfo.getJSONArray("data").size() > 0); } /** @@ -241,10 +306,19 @@ public class ZLMRTPServerFactory { * @return */ public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) { - JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId); + JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtsp", streamId); if (mediaInfo == null) { return 0; } + Integer code = mediaInfo.getInteger("code"); + if ( code < 0) { + logger.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")); + return -1; + } return mediaInfo.getInteger("totalReaderCount"); } @@ -255,12 +329,12 @@ public class ZLMRTPServerFactory { Boolean result = false; JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param); if (jsonObject == null) { - logger.error("停止RTP推流失败: 请检查ZLM服务"); + logger.error("[停止RTP推流] 失败: 请检查ZLM服务"); } else if (jsonObject.getInteger("code") == 0) { result= true; - logger.info("停止RTP推流成功"); + logger.info("[停止RTP推流] 成功"); } else { - logger.error("停止RTP推流失败: " + jsonObject.getString("msg")); + logger.error("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"), JSON.toJSON(param), jsonObject); } return result; } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java index b56287bb..a4b4cb7e 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java @@ -1,29 +1,31 @@ package com.genersoft.iot.vmp.media.zlm; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForServerStarted; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IStreamProxyService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; 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.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; @Component -@Order(value=1) +@Order(value=2) public class ZLMRunner implements CommandLineRunner { private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class); @@ -34,10 +36,7 @@ public class ZLMRunner implements CommandLineRunner { private ZLMRESTfulUtils zlmresTfulUtils; @Autowired - private ZLMHttpHookSubscribe hookSubscribe; - - @Autowired - private IStreamProxyService streamProxyService; + private ZlmHttpHookSubscribe hookSubscribe; @Autowired private EventPublisher publisher; @@ -45,15 +44,12 @@ public class ZLMRunner implements CommandLineRunner { @Autowired private IMediaServerService mediaServerService; - @Autowired - private IRedisCatchStorage redisCatchStorage; - @Autowired private MediaConfig mediaConfig; - @Qualifier("taskExecutor") @Autowired - private ThreadPoolTaskExecutor taskExecutor; + private DynamicTask dynamicTask; + @Override public void run(String... strings) throws Exception { @@ -63,75 +59,95 @@ public class ZLMRunner implements CommandLineRunner { mediaServerService.addToDatabase(mediaConfig.getMediaSerItem()); }else { MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem(); - mediaSerItem.setId(defaultMediaServer.getId()); mediaServerService.updateToDatabase(mediaSerItem); } - + mediaServerService.syncCatchFromDatabase(); + HookSubscribeForServerStarted hookSubscribeForServerStarted = HookSubscribeFactory.on_server_started(); // 订阅 zlm启动事件, 新的zlm也会从这里进入系统 - hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null, + hookSubscribe.addSubscribe(hookSubscribeForServerStarted, (MediaServerItem mediaServerItem, JSONObject response)->{ - ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class); + ZLMServerConfig zlmServerConfig = response.to(ZLMServerConfig.class); if (zlmServerConfig !=null ) { if (startGetMedia != null) { startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId()); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } } - mediaServerService.zlmServerOnline(zlmServerConfig); } }); - // 订阅 zlm保活事件, 当zlm离线时做业务的处理 - hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_keepalive,null, - (MediaServerItem mediaServerItem, JSONObject response)->{ - String mediaServerId = response.getString("mediaServerId"); - if (mediaServerId !=null ) { - mediaServerService.updateMediaServerKeepalive(mediaServerId, response.getJSONObject("data")); - } - }); - // 获取zlm信息 - logger.info("[zlm接入]等待默认zlm中..."); + logger.info("[zlm] 等待默认zlm中..."); // 获取所有的zlm, 并开启主动连接 List all = mediaServerService.getAllFromDatabase(); + Map allMap = new HashMap<>(); + mediaServerService.updateVmServer(all); if (all.size() == 0) { all.add(mediaConfig.getMediaSerItem()); } for (MediaServerItem mediaServerItem : all) { - if (startGetMedia == null) startGetMedia = new HashMap<>(); + if (startGetMedia == null) { + startGetMedia = new ConcurrentHashMap<>(); + } startGetMedia.put(mediaServerItem.getId(), true); - taskExecutor.execute(()->{ - connectZlmServer(mediaServerItem); - }); + connectZlmServer(mediaServerItem); + allMap.put(mediaServerItem.getId(), mediaServerItem); } - Timer timer = new Timer(); - // 10分钟后未连接到则不再去主动连接, TODO 并对重启前使用此在zlm的通道发送bye - timer.schedule(new TimerTask() { - @Override - public void run() { - if (startGetMedia != null) { + String taskKey = "zlm-connect-timeout"; + dynamicTask.startDelay(taskKey, ()->{ + if (startGetMedia != null && startGetMedia.size() > 0) { Set allZlmId = startGetMedia.keySet(); for (String id : allZlmId) { - logger.error("[ {} ]]主动连接失败,不再主动连接", id); + logger.error("[ {} ]]主动连接失败,不再尝试连接", id); } startGetMedia = null; } - // TODO 清理数据库中与redis不匹配的zlm + // 获取redis中所有的zlm + List allInRedis = mediaServerService.getAll(); + for (MediaServerItem mediaServerItem : allInRedis) { + if (!allMap.containsKey(mediaServerItem.getId())) { + mediaServerService.delete(mediaServerItem.getId()); + } } - }, 60 * 1000 * 10); + }, 60 * 1000 ); } - @Async + @Async("taskExecutor") public void connectZlmServer(MediaServerItem mediaServerItem){ - ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem, 1); - if (zlmServerConfig != null) { - zlmServerConfig.setIp(mediaServerItem.getIp()); - zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort()); + String connectZlmServerTaskKey = "connect-zlm-" + mediaServerItem.getId(); + ZLMServerConfig zlmServerConfigFirst = getMediaServerConfig(mediaServerItem); + if (zlmServerConfigFirst != null) { + zlmServerConfigFirst.setIp(mediaServerItem.getIp()); + zlmServerConfigFirst.setHttpPort(mediaServerItem.getHttpPort()); startGetMedia.remove(mediaServerItem.getId()); - mediaServerService.zlmServerOnline(zlmServerConfig); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } + mediaServerService.zlmServerOnline(zlmServerConfigFirst); + }else { + logger.info("[ {} ]-[ {}:{} ]主动连接失败, 清理相关资源, 开始尝试重试连接", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + publisher.zlmOfflineEventPublish(mediaServerItem.getId()); } + + dynamicTask.startCron(connectZlmServerTaskKey, ()->{ + ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem); + if (zlmServerConfig != null) { + dynamicTask.stop(connectZlmServerTaskKey); + zlmServerConfig.setIp(mediaServerItem.getIp()); + zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort()); + startGetMedia.remove(mediaServerItem.getId()); + if (startGetMedia.size() == 0) { + hookSubscribe.removeSubscribe(HookSubscribeFactory.on_server_started()); + } + mediaServerService.zlmServerOnline(zlmServerConfig); + } + }, 2000); } - public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem, int index) { + public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) { if (startGetMedia == null) { return null;} if (!mediaServerItem.isDefaultServer() && mediaServerService.getOne(mediaServerItem.getId()) == null) { return null; @@ -139,62 +155,18 @@ public class ZLMRunner implements CommandLineRunner { if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) { return null; } - JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); - ZLMServerConfig ZLMServerConfig = null; - if (responseJSON != null) { - JSONArray data = responseJSON.getJSONArray("data"); + JSONObject responseJson = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); + ZLMServerConfig zlmServerConfig = null; + if (responseJson != null) { + JSONArray data = responseJson.getJSONArray("data"); if (data != null && data.size() > 0) { - ZLMServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); - ZLMServerConfig.setIp(mediaServerItem.getIp()); + zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); } } else { - logger.error("[ {} ]-[ {}:{} ]第{}次主动连接失败, 2s后重试", - mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort(), index); - if (index == 1 && !StringUtils.isEmpty(mediaServerItem.getId())) { - logger.info("[ {} ]-[ {}:{} ]第{}次主动连接失败, 开始清理相关资源", - mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort(), index); - publisher.zlmOfflineEventPublish(mediaServerItem.getId()); - } - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - ZLMServerConfig = getMediaServerConfig(mediaServerItem, index += 1); + logger.error("[ {} ]-[ {}:{} ]主动连接失败, 2s后重试", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); } - return ZLMServerConfig; + return zlmServerConfig; } - - /** - * zlm 连接成功或者zlm重启后 - */ -// private void zLmRunning(ZLMServerConfig zlmServerConfig){ -// logger.info( "[ id: " + zlmServerConfig.getGeneralMediaServerId() + "] zlm接入成功..."); -// // 关闭循环获取zlm配置 -// startGetMedia = false; -// MediaServerItem mediaServerItem = new MediaServerItem(zlmServerConfig, sipIp); -// storager.updateMediaServer(mediaServerItem); -// -// if (mediaServerItem.isAutoConfig()) setZLMConfig(mediaServerItem); -// zlmServerManger.updateServerCatchFromHook(zlmServerConfig); -// -// // 清空所有session -//// zlmMediaListManager.clearAllSessions(); -// -// // 更新流列表 -// zlmMediaListManager.updateMediaList(mediaServerItem); -// // 恢复流代理, 只查找这个这个流媒体 -// List streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer( -// mediaServerItem.getId(), true); -// for (StreamProxyItem streamProxyDto : streamProxyListForEnable) { -// logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); -// JSONObject jsonObject = streamProxyService.addStreamProxyToZlm(streamProxyDto); -// if (jsonObject == null) { -// // 设置为未启用 -// logger.info("恢复流代理失败,请检查流地址后重新启用" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); -// streamProxyService.stop(streamProxyDto.getApp(), streamProxyDto.getStream()); -// } -// } -// } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java index 7e3da46a..fcf24012 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.media.zlm; -import com.alibaba.fastjson.annotation.JSONField; +import com.alibaba.fastjson2.annotation.JSONField; public class ZLMServerConfig { @@ -66,7 +66,7 @@ public class ZLMServerConfig { private String hookAdminParams; @JSONField(name = "hook.alive_interval") - private int hookAliveInterval; + private Float hookAliveInterval; @JSONField(name = "hook.enable") private String hookEnable; @@ -194,6 +194,9 @@ public class ZLMServerConfig { @JSONField(name = "rtp_proxy.port") private int rtpProxyPort; + @JSONField(name = "rtp_proxy.port_range") + private String portRange; + @JSONField(name = "rtp_proxy.timeoutSec") private String rtpProxyTimeoutSec; @@ -795,11 +798,19 @@ public class ZLMServerConfig { this.shellPhell = shellPhell; } - public int getHookAliveInterval() { + public Float getHookAliveInterval() { return hookAliveInterval; } - public void setHookAliveInterval(int hookAliveInterval) { + public void setHookAliveInterval(Float hookAliveInterval) { this.hookAliveInterval = hookAliveInterval; } + + public String getPortRange() { + return portRange; + } + + public void setPortRange(String portRange) { + this.portRange = portRange; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZlmHttpHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZlmHttpHookSubscribe.java new file mode 100644 index 00000000..cf33bb24 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZlmHttpHookSubscribe.java @@ -0,0 +1,155 @@ +package com.genersoft.iot.vmp.media.zlm; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.media.zlm.dto.HookType; +import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +/** + * ZLMediaServer的hook事件订阅 + * @author lin + */ +@Component +public class ZlmHttpHookSubscribe { + + private final static Logger logger = LoggerFactory.getLogger(ZlmHttpHookSubscribe.class); + + @FunctionalInterface + public interface Event{ + void response(MediaServerItem mediaServerItem, JSONObject response); + } + + private Map> allSubscribes = new ConcurrentHashMap<>(); + + public void addSubscribe(IHookSubscribe hookSubscribe, ZlmHttpHookSubscribe.Event event) { + if (hookSubscribe.getExpires() == null) { + // 默认5分钟过期 + Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(5)); + hookSubscribe.setExpires(expiresInstant); + } + allSubscribes.computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()).put(hookSubscribe, event); + } + + public ZlmHttpHookSubscribe.Event sendNotify(HookType type, JSONObject hookResponse) { + ZlmHttpHookSubscribe.Event event= null; + Map eventMap = allSubscribes.get(type); + if (eventMap == null) { + return null; + } + for (IHookSubscribe key : eventMap.keySet()) { + Boolean result = null; + for (String s : key.getContent().keySet()) { + if (result == null) { + result = key.getContent().getString(s).equals(hookResponse.getString(s)); + }else { + if (key.getContent().getString(s) == null) { + continue; + } + result = result && key.getContent().getString(s).equals(hookResponse.getString(s)); + } + } + if (null != result && result) { + event = eventMap.get(key); + } + } + return event; + } + + public void removeSubscribe(IHookSubscribe hookSubscribe) { + Map eventMap = allSubscribes.get(hookSubscribe.getHookType()); + if (eventMap == null) { + return; + } + + Set> entries = eventMap.entrySet(); + if (entries.size() > 0) { + List> entriesToRemove = new ArrayList<>(); + for (Map.Entry entry : entries) { + JSONObject content = entry.getKey().getContent(); + if (content == null || content.size() == 0) { + entriesToRemove.add(entry); + continue; + } + Boolean result = null; + for (String s : content.keySet()) { + if (result == null) { + result = content.getString(s).equals(hookSubscribe.getContent().getString(s)); + }else { + if (content.getString(s) == null) { + continue; + } + result = result && content.getString(s).equals(hookSubscribe.getContent().getString(s)); + } + } + if (result){ + entriesToRemove.add(entry); + } + } + + if (!CollectionUtils.isEmpty(entriesToRemove)) { + for (Map.Entry entry : entriesToRemove) { + entries.remove(entry); + } + } + + } + } + + /** + * 获取某个类型的所有的订阅 + * @param type + * @return + */ + public List getSubscribes(HookType type) { + Map eventMap = allSubscribes.get(type); + if (eventMap == null) { + return null; + } + List result = new ArrayList<>(); + for (IHookSubscribe key : eventMap.keySet()) { + result.add(eventMap.get(key)); + } + return result; + } + + public List getAll(){ + ArrayList result = new ArrayList<>(); + Collection> values = allSubscribes.values(); + for (Map value : values) { + result.addAll(value.keySet()); + } + return result; + } + + /** + * 对订阅数据进行过期清理 + */ + @Scheduled(cron="0 0/5 * * * ?") //每5分钟执行一次 + public void execute(){ + + Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5)); + int total = 0; + for (HookType hookType : allSubscribes.keySet()) { + Map hookSubscribeEventMap = allSubscribes.get(hookType); + if (hookSubscribeEventMap.size() > 0) { + for (IHookSubscribe hookSubscribe : hookSubscribeEventMap.keySet()) { + if (hookSubscribe.getExpires().isBefore(instant)) { + // 过期的 + hookSubscribeEventMap.remove(hookSubscribe); + total ++; + } + } + } + } + } +} 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 new file mode 100644 index 00000000..714838ed --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ChannelOnlineEvent.java @@ -0,0 +1,11 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import java.text.ParseException; + +/** + * @author lin + */ +public interface ChannelOnlineEvent { + + void run(String app, String stream, String serverId) throws ParseException; +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java new file mode 100644 index 00000000..f3f389a7 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + + +import com.alibaba.fastjson2.JSONObject; + +/** + * hook 订阅工厂 + * @author lin + */ +public class HookSubscribeFactory { + + public static HookSubscribeForStreamChange on_stream_changed(String app, String stream, boolean regist, String scheam, String mediaServerId) { + HookSubscribeForStreamChange hookSubscribe = new HookSubscribeForStreamChange(); + JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject(); + subscribeKey.put("app", app); + subscribeKey.put("stream", stream); + subscribeKey.put("regist", regist); + if (scheam != null) { + subscribeKey.put("schema", scheam); + } + subscribeKey.put("mediaServerId", mediaServerId); + hookSubscribe.setContent(subscribeKey); + + return hookSubscribe; + } + + public static HookSubscribeForRtpServerTimeout on_rtp_server_timeout(String stream, String ssrc, String mediaServerId) { + HookSubscribeForRtpServerTimeout hookSubscribe = new HookSubscribeForRtpServerTimeout(); + JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject(); + subscribeKey.put("stream_id", stream); + subscribeKey.put("ssrc", ssrc); + subscribeKey.put("mediaServerId", mediaServerId); + hookSubscribe.setContent(subscribeKey); + + return hookSubscribe; + } + + public static HookSubscribeForServerStarted on_server_started() { + HookSubscribeForServerStarted hookSubscribe = new HookSubscribeForServerStarted(); + hookSubscribe.setContent(new JSONObject()); + + return hookSubscribe; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRtpServerTimeout.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRtpServerTimeout.java new file mode 100644 index 00000000..d633560d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRtpServerTimeout.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.annotation.JSONField; + +import java.time.Instant; + +/** + * hook订阅-收流超时 + * @author lin + */ +public class HookSubscribeForRtpServerTimeout implements IHookSubscribe{ + + private HookType hookType = HookType.on_rtp_server_timeout; + + private JSONObject content; + + @JSONField(format="yyyy-MM-dd HH:mm:ss") + private Instant expires; + + @Override + public HookType getHookType() { + return hookType; + } + + @Override + public JSONObject getContent() { + return content; + } + + public void setContent(JSONObject content) { + this.content = content; + } + + @Override + public Instant getExpires() { + return expires; + } + + @Override + public void setExpires(Instant expires) { + this.expires = expires; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java new file mode 100644 index 00000000..8bcde0a0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.annotation.JSONField; + +import java.time.Instant; + +/** + * hook订阅-流变化 + * @author lin + */ +public class HookSubscribeForServerStarted implements IHookSubscribe{ + + private HookType hookType = HookType.on_server_started; + + private JSONObject content; + + @JSONField(format="yyyy-MM-dd HH:mm:ss") + private Instant expires; + + @Override + public HookType getHookType() { + return hookType; + } + + @Override + public JSONObject getContent() { + return content; + } + + public void setContent(JSONObject content) { + this.content = content; + } + + @Override + public Instant getExpires() { + return expires; + } + + @Override + public void setExpires(Instant expires) { + this.expires = expires; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java new file mode 100644 index 00000000..b73d74c1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java @@ -0,0 +1,44 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.annotation.JSONField; + +import java.time.Instant; + +/** + * hook订阅-流变化 + * @author lin + */ +public class HookSubscribeForStreamChange implements IHookSubscribe{ + + private HookType hookType = HookType.on_stream_changed; + + private JSONObject content; + + @JSONField(format="yyyy-MM-dd HH:mm:ss") + private Instant expires; + + @Override + public HookType getHookType() { + return hookType; + } + + @Override + public JSONObject getContent() { + return content; + } + + public void setContent(JSONObject content) { + this.content = content; + } + + @Override + public Instant getExpires() { + return expires; + } + + @Override + public void setExpires(Instant expires) { + this.expires = expires; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java new file mode 100644 index 00000000..a4557e9a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java @@ -0,0 +1,25 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +/** + * hook类型 + * @author lin + */ + +public enum HookType { + + on_flow_report, + on_http_access, + on_play, + on_publish, + on_record_mp4, + on_rtsp_auth, + on_rtsp_realm, + on_shell_login, + on_stream_changed, + on_stream_none_reader, + on_stream_not_found, + on_server_started, + + on_rtp_server_timeout, + on_server_keepalive +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java new file mode 100644 index 00000000..7b76a959 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java @@ -0,0 +1,36 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import com.alibaba.fastjson2.JSONObject; + +import java.time.Instant; + +/** + * zlm hook事件的参数 + * @author lin + */ +public interface IHookSubscribe { + + /** + * 获取hook类型 + * @return hook类型 + */ + HookType getHookType(); + + /** + * 获取hook的具体内容 + * @return hook的具体内容 + */ + JSONObject getContent(); + + /** + * 设置过期时间 + * @param instant 过期时间 + */ + void setExpires(Instant instant); + + /** + * 获取过期时间 + * @return 过期时间 + */ + Instant getExpires(); +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java index 260da272..f3eb3d67 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java @@ -3,64 +3,87 @@ package com.genersoft.iot.vmp.media.zlm.dto; import com.genersoft.iot.vmp.gb28181.session.SsrcConfig; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; -import org.springframework.util.StringUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.util.ObjectUtils; import java.util.HashMap; +@Schema(description = "流媒体服务信息") public class MediaServerItem{ + @Schema(description = "ID") private String id; + @Schema(description = "IP") private String ip; + @Schema(description = "hook使用的IP(zlm访问WVP使用的IP)") private String hookIp; + @Schema(description = "SDP IP") private String sdpIp; + @Schema(description = "流IP") private String streamIp; + @Schema(description = "HTTP端口") private int httpPort; + @Schema(description = "HTTPS端口") private int httpSSlPort; + @Schema(description = "RTMP端口") private int rtmpPort; + @Schema(description = "RTMPS端口") private int rtmpSSlPort; + @Schema(description = "RTP收流端口(单端口模式有用)") private int rtpProxyPort; + @Schema(description = "RTSP端口") private int rtspPort; + @Schema(description = "RTSPS端口") private int rtspSSLPort; + @Schema(description = "是否开启自动配置ZLM") private boolean autoConfig; + @Schema(description = "ZLM鉴权参数") private String secret; - private int streamNoneReaderDelayMS; - - private int hookAliveInterval; + @Schema(description = "keepalive hook触发间隔,单位秒") + private Float hookAliveInterval; + @Schema(description = "是否使用多端口模式") private boolean rtpEnable; + @Schema(description = "状态") private boolean status; + @Schema(description = "多端口RTP收流端口范围") private String rtpPortRange; - private String sendRtpPortRange; - + @Schema(description = "assist服务端口") private int recordAssistPort; + @Schema(description = "创建时间") private String createTime; + @Schema(description = "更新时间") private String updateTime; + @Schema(description = "上次心跳时间") private String lastKeepaliveTime; + @Schema(description = "是否是默认ZLM") private boolean defaultServer; + @Schema(description = "SSRC信息") private SsrcConfig ssrcConfig; + @Schema(description = "当前使用到的端口") private int currentPort; @@ -68,6 +91,7 @@ public class MediaServerItem{ * 每一台ZLM都有一套独立的SSRC列表 * 在ApplicationCheckRunner里对mediaServerSsrcMap进行初始化 */ + @Schema(description = "ID") private HashMap mediaServerSsrcMap; public MediaServerItem() { @@ -76,9 +100,9 @@ public class MediaServerItem{ public MediaServerItem(ZLMServerConfig zlmServerConfig, String sipIp) { id = zlmServerConfig.getGeneralMediaServerId(); ip = zlmServerConfig.getIp(); - hookIp = StringUtils.isEmpty(zlmServerConfig.getHookIp())? sipIp: zlmServerConfig.getHookIp(); - sdpIp = StringUtils.isEmpty(zlmServerConfig.getSdpIp())? zlmServerConfig.getIp(): zlmServerConfig.getSdpIp(); - streamIp = StringUtils.isEmpty(zlmServerConfig.getStreamIp())? zlmServerConfig.getIp(): zlmServerConfig.getStreamIp(); + hookIp = ObjectUtils.isEmpty(zlmServerConfig.getHookIp())? sipIp: zlmServerConfig.getHookIp(); + sdpIp = ObjectUtils.isEmpty(zlmServerConfig.getSdpIp())? zlmServerConfig.getIp(): zlmServerConfig.getSdpIp(); + streamIp = ObjectUtils.isEmpty(zlmServerConfig.getStreamIp())? zlmServerConfig.getIp(): zlmServerConfig.getStreamIp(); httpPort = zlmServerConfig.getHttpPort(); httpSSlPort = zlmServerConfig.getHttpSSLport(); rtmpPort = zlmServerConfig.getRtmpPort(); @@ -88,11 +112,9 @@ public class MediaServerItem{ rtspSSLPort = zlmServerConfig.getRtspSSlport(); autoConfig = true; // 默认值true; secret = zlmServerConfig.getApiSecret(); - streamNoneReaderDelayMS = zlmServerConfig.getGeneralStreamNoneReaderDelayMS(); hookAliveInterval = zlmServerConfig.getHookAliveInterval(); rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 - rtpPortRange = "30000,30500"; // 默认使用30000,30500作为级联时发送流的端口号 - sendRtpPortRange = "30000,30500"; // 默认使用30000,30500作为级联时发送流的端口号 + rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号 recordAssistPort = 0; // 默认关闭 } @@ -209,14 +231,6 @@ public class MediaServerItem{ this.secret = secret; } - public int getStreamNoneReaderDelayMS() { - return streamNoneReaderDelayMS; - } - - public void setStreamNoneReaderDelayMS(int streamNoneReaderDelayMS) { - this.streamNoneReaderDelayMS = streamNoneReaderDelayMS; - } - public boolean isRtpEnable() { return rtpEnable; } @@ -305,19 +319,11 @@ public class MediaServerItem{ this.lastKeepaliveTime = lastKeepaliveTime; } - public String getSendRtpPortRange() { - return sendRtpPortRange; - } - - public void setSendRtpPortRange(String sendRtpPortRange) { - this.sendRtpPortRange = sendRtpPortRange; - } - - public int getHookAliveInterval() { + public Float getHookAliveInterval() { return hookAliveInterval; } - public void setHookAliveInterval(int hookAliveInterval) { + public void setHookAliveInterval(Float hookAliveInterval) { this.hookAliveInterval = hookAliveInterval; } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ServerKeepaliveData.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ServerKeepaliveData.java new file mode 100644 index 00000000..0cc81f27 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ServerKeepaliveData.java @@ -0,0 +1,4 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +public class ServerKeepaliveData { +} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamAuthorityInfo.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamAuthorityInfo.java new file mode 100644 index 00000000..ef77225d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamAuthorityInfo.java @@ -0,0 +1,117 @@ +package com.genersoft.iot.vmp.media.zlm.dto; + +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnPublishHookParam; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; + +/** + * 流的鉴权信息 + * @author lin + */ +public class StreamAuthorityInfo { + + private String id; + private String app; + private String stream; + + /** + * 产生源类型, + * unknown = 0, + * rtmp_push=1, + * rtsp_push=2, + * rtp_push=3, + * pull=4, + * ffmpeg_pull=5, + * mp4_vod=6, + * device_chn=7 + */ + private int originType; + + /** + * 产生源类型的字符串描述 + */ + private String originTypeStr; + + /** + * 推流时自定义的播放鉴权ID + */ + private String callId; + + /** + * 推流的鉴权签名 + */ + private String sign; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getStream() { + return stream; + } + + public void setStream(String stream) { + this.stream = stream; + } + + public int getOriginType() { + return originType; + } + + public void setOriginType(int originType) { + this.originType = originType; + } + + public String getOriginTypeStr() { + return originTypeStr; + } + + public void setOriginTypeStr(String originTypeStr) { + this.originTypeStr = originTypeStr; + } + + public String getCallId() { + return callId; + } + + public void setCallId(String callId) { + this.callId = callId; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } + + public static StreamAuthorityInfo getInstanceByHook(OnPublishHookParam hookParam) { + StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo(); + streamAuthorityInfo.setApp(hookParam.getApp()); + streamAuthorityInfo.setStream(hookParam.getStream()); + streamAuthorityInfo.setId(hookParam.getId()); + return streamAuthorityInfo; + } + + public static StreamAuthorityInfo getInstanceByHook(OnStreamChangedHookParam onStreamChangedHookParam) { + StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo(); + streamAuthorityInfo.setApp(onStreamChangedHookParam.getApp()); + streamAuthorityInfo.setStream(onStreamChangedHookParam.getStream()); + streamAuthorityInfo.setId(onStreamChangedHookParam.getMediaServerId()); + streamAuthorityInfo.setOriginType(onStreamChangedHookParam.getOriginType()); + streamAuthorityInfo.setOriginTypeStr(onStreamChangedHookParam.getOriginTypeStr()); + return streamAuthorityInfo; + } +} 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 index 38e44a98..b0e74e8f 100644 --- 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 @@ -1,24 +1,46 @@ 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 src_url; + @Schema(description = "目标地址") private String dst_url; + @Schema(description = "超时时间") private int timeout_ms; + @Schema(description = "ffmpeg模板KEY") private String ffmpeg_cmd_key; + @Schema(description = "rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播") private String rtp_type; + @Schema(description = "是否启用") private boolean enable; - private boolean enable_hls; + @Schema(description = "是否启用音频") + private boolean enable_audio; + @Schema(description = "是否启用MP4") private boolean enable_mp4; - private boolean enable_remove_none_reader; // 无人观看时删除 - private String platformGbId; + @Schema(description = "是否 无人观看时删除") + private boolean enable_remove_none_reader; + + @Schema(description = "是否 无人观看时自动停用") + private boolean enable_disable_none_reader; + @Schema(description = "创建时间") private String createTime; public String getType() { @@ -29,18 +51,22 @@ public class StreamProxyItem extends GbStream { 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; } @@ -111,14 +137,6 @@ public class StreamProxyItem extends GbStream { this.enable = enable; } - public boolean isEnable_hls() { - return enable_hls; - } - - public void setEnable_hls(boolean enable_hls) { - this.enable_hls = enable_hls; - } - public boolean isEnable_mp4() { return enable_mp4; } @@ -127,19 +145,12 @@ public class StreamProxyItem extends GbStream { this.enable_mp4 = enable_mp4; } - - public String getPlatformGbId() { - return platformGbId; - } - - public void setPlatformGbId(String platformGbId) { - this.platformGbId = platformGbId; - } - + @Override public String getCreateTime() { return createTime; } + @Override public void setCreateTime(String createTime) { this.createTime = createTime; } @@ -151,4 +162,20 @@ public class StreamProxyItem extends GbStream { public void setEnable_remove_none_reader(boolean enable_remove_none_reader) { this.enable_remove_none_reader = enable_remove_none_reader; } + + public boolean isEnable_disable_none_reader() { + return enable_disable_none_reader; + } + + public void setEnable_disable_none_reader(boolean enable_disable_none_reader) { + this.enable_disable_none_reader = enable_disable_none_reader; + } + + public boolean isEnable_audio() { + return enable_audio; + } + + public void setEnable_audio(boolean enable_audio) { + this.enable_audio = enable_audio; + } } 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 index 73e162af..ddcfbdd9 100644 --- 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 @@ -1,31 +1,44 @@ package com.genersoft.iot.vmp.media.zlm.dto; import com.genersoft.iot.vmp.gb28181.bean.GbStream; +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 String totalReaderCount; /** * 协议 包括hls/rtsp/rtmp/http-flv/ws-flv */ + @Schema(description = "协议 包括hls/rtsp/rtmp/http-flv/ws-flv") private List schemas; /** @@ -39,48 +52,89 @@ public class StreamPushItem extends GbStream implements Comparable tracks; + @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; } @@ -92,7 +146,8 @@ public class StreamPushItem extends GbStream implements Comparable getTracks() { + public List getTracks() { return tracks; } - public void setTracks(List tracks) { + public void setTracks(List tracks) { this.tracks = tracks; } @@ -216,5 +274,56 @@ public class StreamPushItem extends GbStream implements Comparable { - - private final static Logger logger = LoggerFactory.getLogger(ZLMOfflineEventListener.class); - - @Autowired - private IMediaServerService mediaServerService; - - @Autowired - private IStreamPushService streamPushService; - - @Autowired - private IStreamProxyService streamProxyService; - - @Override - public void onApplicationEvent(ZLMOfflineEvent event) { - - logger.info("ZLM离线事件触发,ID:" + event.getMediaServerId()); - // 处理ZLM离线 - mediaServerService.zlmServerOffline(event.getMediaServerId()); - streamProxyService.zlmServerOffline(event.getMediaServerId()); - streamPushService.zlmServerOffline(event.getMediaServerId()); - // TODO 处理对国标的影响 - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEventListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java similarity index 53% rename from src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEventListener.java rename to src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java index 952a673c..bad8e560 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMOnlineEventListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/event/ZLMStatusEventListener.java @@ -1,19 +1,16 @@ package com.genersoft.iot.vmp.media.zlm.event; -import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.conf.UserSetup; import com.genersoft.iot.vmp.service.IMediaServerService; +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.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationListener; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import java.text.SimpleDateFormat; /** * @description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源: @@ -23,9 +20,9 @@ import java.text.SimpleDateFormat; * @date: 2020年5月6日 下午1:51:23 */ @Component -public class ZLMOnlineEventListener implements ApplicationListener { +public class ZLMStatusEventListener { - private final static Logger logger = LoggerFactory.getLogger(ZLMOnlineEventListener.class); + private final static Logger logger = LoggerFactory.getLogger(ZLMStatusEventListener.class); @Autowired private IStreamPushService streamPushService; @@ -33,14 +30,30 @@ public class ZLMOnlineEventListener implements ApplicationListener channels); + + /** + * 获取统计信息 + * @return + */ + ResourceBaceInfo getOverview(); + + /** + * 查询所有未分配的通道 + * @param platformId + * @return + */ + List queryAllChannelList(String platformId); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java b/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java index 0cb84136..b87c9a73 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java @@ -1,24 +1,166 @@ 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.SyncStatus; +import com.genersoft.iot.vmp.vmanager.bean.BaseTree; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; + +import java.util.List; /** * 设备相关业务处理 + * @author lin */ public interface IDeviceService { + /** + * 设备上线 + * @param device 设备信息 + */ + void online(Device device); + + /** + * 设备下线 + * @param deviceId 设备编号 + */ + void offline(String deviceId); + /** * 添加目录订阅 * @param device 设备信息 - * @return + * @return 布尔 */ boolean addCatalogSubscribe(Device device); /** * 移除目录订阅 * @param device 设备信息 - * @return + * @return 布尔 */ boolean removeCatalogSubscribe(Device device); + /** + * 添加移动位置订阅 + * @param device 设备信息 + * @return 布尔 + */ + boolean addMobilePositionSubscribe(Device device); + + /** + * 移除移动位置订阅 + * @param device 设备信息 + * @return 布尔 + */ + boolean removeMobilePositionSubscribe(Device device); + + /** + * 移除移动位置订阅 + * @param deviceId 设备ID + * @return 同步状态 + */ + SyncStatus getChannelSyncStatus(String deviceId); + + /** + * 查看是否仍在同步 + * @param deviceId 设备ID + * @return 布尔 + */ + Boolean isSyncRunning(String deviceId); + + /** + * 通道同步 + * @param device 设备信息 + */ + void sync(Device device); + + /** + * 查询设备信息 + * @param deviceId 设备编号 + * @return 设备信息 + */ + Device getDevice(String deviceId); + + /** + * 获取所有在线设备 + * @return 设备列表 + */ + List getAllOnlineDevice(); + + /** + * 判断是否注册已经失效 + * @param device 设备信息 + * @return 布尔 + */ + boolean expire(Device device); + + /** + * 检查设备状态 + * @param device 设备信息 + */ + void checkDeviceStatus(Device device); + + /** + * 根据IP和端口获取设备信息 + * @param host IP + * @param port 端口 + * @return 设备信息 + */ + Device getDeviceByHostAndPort(String host, int port); + + /** + * 更新设备 + * @param device 设备信息 + */ + 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 设备编号 + * @return + */ + boolean isExist(String deviceId); + + /** + * 添加设备 + * @param device + */ + void addDevice(Device device); + + /** + * 页面表单更新设备信息 + * @param device + */ + void updateCustomDevice(Device device); + + /** + * 删除设备 + * @param deviceId + * @return + */ + boolean delete(String deviceId); + + /** + * 获取统计信息 + * @return + */ + ResourceBaceInfo getOverview(); + } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java b/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java index 49ba7b7f..314a3890 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IGbStreamService.java @@ -1,6 +1,9 @@ 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; @@ -16,7 +19,7 @@ public interface IGbStreamService { * @param count * @return */ - PageInfo getAll(Integer page, Integer count); + PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId,String query,String mediaServerId); /** @@ -30,11 +33,40 @@ public interface IGbStreamService { * 保存国标关联 * @param gbStreams */ - boolean addPlatformInfo(List gbStreams, String platformId); + boolean addPlatformInfo(List gbStreams, String platformId, String catalogId); /** * 移除国标关联 * @param gbStreams + * @param platformId */ - boolean delPlatformInfo(List gbStreams); + 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); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java index fcba07f8..1233455f 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java @@ -1,11 +1,10 @@ package com.genersoft.iot.vmp.service; -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData; +import com.genersoft.iot.vmp.service.bean.MediaServerLoad; import com.genersoft.iot.vmp.service.bean.SSRCInfo; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import java.util.List; @@ -22,7 +21,7 @@ public interface IMediaServerService { MediaServerItem getOne(String generalMediaServerId); - MediaServerItem getOneByHostAndPort(String host, int port); + void syncCatchFromDatabase(); /** * 新的节点加入 @@ -38,15 +37,21 @@ public interface IMediaServerService { */ void zlmServerOffline(String mediaServerId); - MediaServerItem getMediaServerForMinimumLoad(); + MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist); - void setZLMConfig(MediaServerItem mediaServerItem); + void setZLMConfig(MediaServerItem mediaServerItem, boolean restart); - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId); + void updateVmServer(List mediaServerItemList); - SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean isPlayback); + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback); - void closeRTPServer(Device device, String channelId); + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback); + + SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback, Integer port); + + void closeRTPServer(MediaServerItem mediaServerItem, String streamId); + + void closeRTPServer(String mediaServerId, String streamId); void clearRTPServer(MediaServerItem mediaServerItem); @@ -56,11 +61,11 @@ public interface IMediaServerService { void removeCount(String mediaServerId); - void releaseSsrc(MediaServerItem mediaServerItem, String ssrc); + void releaseSsrc(String mediaServerItemId, String ssrc); void clearMediaServerForOnline(); - WVPResult add(MediaServerItem mediaSerItem); + void add(MediaServerItem mediaSerItem); int addToDatabase(MediaServerItem mediaSerItem); @@ -68,13 +73,23 @@ public interface IMediaServerService { void resetOnlineServerItem(MediaServerItem serverItem); - WVPResult checkMediaServer(String ip, int port, String secret); + MediaServerItem checkMediaServer(String ip, int port, String secret); boolean checkMediaRecordServer(String ip, int port); void delete(String id); + void deleteDb(String id); + MediaServerItem getDefaultMediaServer(); - void updateMediaServerKeepalive(String zlmServerConfig, JSONObject data); + void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data); + + boolean checkRtpServer(MediaServerItem mediaServerItem, String rtp, String stream); + + /** + * 获取负载信息 + * @return + */ + MediaServerLoad getLoad(MediaServerItem mediaServerItem); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java b/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java index 8c05b85f..c05f1977 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IMediaService.java @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.service; -import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson2.JSONArray; import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; @@ -15,7 +15,7 @@ public interface IMediaService { * @param stream * @return */ - StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId,String addr); + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId,String addr, boolean authority); /** @@ -24,7 +24,7 @@ public interface IMediaService { * @param stream * @return */ - StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId); + StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority); /** * 根据应用名和流ID获取播放地址, 只是地址拼接 @@ -32,7 +32,7 @@ public interface IMediaService { * @param stream * @return */ - StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, Object tracks); + StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaServerItem, String app, String stream, Object tracks, String callId); /** * 根据应用名和流ID获取播放地址, 只是地址拼接,返回的ip使用远程访问ip,适用与zlm与wvp在一台主机的情况 @@ -40,5 +40,5 @@ public interface IMediaService { * @param stream * @return */ - StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr); + StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr, String callId); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java new file mode 100644 index 00000000..49d74282 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlatformChannelService.java @@ -0,0 +1,29 @@ +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/IPlatformService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java new file mode 100644 index 00000000..17f8b37f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java @@ -0,0 +1,51 @@ +package com.genersoft.iot.vmp.service; + +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.github.pagehelper.PageInfo; + +/** + * 国标平台的业务类 + * @author lin + */ +public interface IPlatformService { + + ParentPlatform queryPlatformByServerGBId(String platformGbId); + + /** + * 分页获取上级平台 + * @param page + * @param count + * @return + */ + PageInfo queryParentPlatformList(int page, int count); + + /** + * 添加级联平台 + * @param parentPlatform 级联平台 + */ + boolean add(ParentPlatform parentPlatform); + + /** + * 平台上线 + * @param parentPlatform 平台信息 + */ + void online(ParentPlatform parentPlatform); + + /** + * 平台离线 + * @param parentPlatform 平台信息 + */ + void offline(ParentPlatform parentPlatform, boolean stopRegisterTask); + + /** + * 向上级平台发起注册 + * @param parentPlatform + */ + void login(ParentPlatform parentPlatform); + + /** + * 向上级平台发送位置订阅 + * @param platformId 平台 + */ + void sendNotifyMobilePosition(String platformId); +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java index 8a7437cd..ad59cb6e 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IPlayService.java @@ -1,23 +1,56 @@ package com.genersoft.iot.vmp.service; -import com.alibaba.fastjson.JSONObject; +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.InviteStreamCallback; +import com.genersoft.iot.vmp.gb28181.bean.InviteStreamInfo; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; -import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; +import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; +import com.genersoft.iot.vmp.service.bean.PlayBackCallback; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; /** * 点播处理 */ public interface IPlayService { - void onPublishHandlerForPlayBack(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid); - void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid); + void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId); - PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent); + void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, + InviteTimeOutCallback timeoutCallback); + void play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZlmHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent, Runnable timeoutCallback); MediaServerItem getNewMediaServerItem(Device device); - void onPublishHandlerForDownload(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, String toString); + /** + * 获取包含assist服务的节点 + */ + MediaServerItem getNewMediaServerItemHasAssist(Device device); + + void onPublishHandlerForDownload(InviteStreamInfo inviteStreamInfo, String deviceId, String channelId, String toString); + + void playBack(String deviceId, String channelId, String startTime, String endTime, InviteStreamCallback infoCallBack, PlayBackCallback playBackCallback); + void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack); + + void zlmServerOffline(String mediaServerId); + + void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback playBackCallback); + void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo,String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack); + + StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream); + + void zlmServerOnline(String mediaServerId); + + void pauseRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; + + void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException; } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java b/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java index 40b2c9a6..de9613eb 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IStreamProxyService.java @@ -1,13 +1,10 @@ package com.genersoft.iot.vmp.service; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; -import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; import com.github.pagehelper.PageInfo; public interface IStreamProxyService { @@ -16,7 +13,7 @@ public interface IStreamProxyService { * 保存视频代理 * @param param */ - WVPResult save(StreamProxyItem param); + StreamInfo save(StreamProxyItem param); /** * 添加视频代理到zlm @@ -55,6 +52,16 @@ public interface IStreamProxyService { */ boolean start(String app, String stream); + /** + * 更新状态 + * @param status 状态 + * @param app + * @param stream + */ + int updateStatus(boolean status, String app, String stream); + + + /** * 停用用视频代理 * @param app @@ -91,4 +98,16 @@ public interface IStreamProxyService { void zlmServerOffline(String mediaServerId); void clean(); + + /** + * 更新代理流 + */ + boolean updateStreamProxy(StreamProxyItem streamProxyItem); + + /** + * 获取统计信息 + * @return + */ + ResourceBaceInfo getOverview(); + } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java b/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java index 06c8b732..cf6f0ed2 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IStreamPushService.java @@ -1,14 +1,19 @@ package com.genersoft.iot.vmp.service; import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; -import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; import com.github.pagehelper.PageInfo; import java.util.List; +import java.util.Map; +/** + * @author lin + */ public interface IStreamPushService { List handleJSON(String json, MediaServerItem mediaServerItem); @@ -29,14 +34,12 @@ public interface IStreamPushService { /** * 获取 - * @param page - * @param count - * @return */ - PageInfo getPushList(Integer page, Integer count); + PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId); + List getPushList(String mediaSererId); - StreamPushItem transform(MediaItem item); + StreamPushItem transform(OnStreamChangedHookParam item); StreamPushItem getPush(String app, String streamId); @@ -44,25 +47,71 @@ public interface IStreamPushService { * 停止一路推流 * @param app 应用名 * @param streamId 流ID - * @return */ boolean stop(String app, String streamId); /** * 新的节点加入 - * @param mediaServerId - * @return */ void zlmServerOnline(String mediaServerId); /** * 节点离线 - * @param mediaServerId - * @return */ 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 + */ + ResourceBaceInfo getOverview(); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/IUserService.java b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java index c170c021..7e2a8395 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/IUserService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/IUserService.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.service; import com.genersoft.iot.vmp.storager.dao.dto.User; +import com.github.pagehelper.PageInfo; import java.util.List; @@ -19,4 +20,10 @@ public interface IUserService { List getAllUsers(); int updateUsers(User user); + + boolean checkPushAuthority(String callId, String sign); + + PageInfo getUsers(int page, int count); + + int changePushKey(int id, String pushKey); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/CatalogSubscribeTask.java b/src/main/java/com/genersoft/iot/vmp/service/bean/CatalogSubscribeTask.java deleted file mode 100644 index cfaef712..00000000 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/CatalogSubscribeTask.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.genersoft.iot.vmp.service.bean; - -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; -import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; -import org.dom4j.DocumentException; -import org.dom4j.Element; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.sip.ResponseEvent; - -public class CatalogSubscribeTask implements Runnable{ - private final Logger logger = LoggerFactory.getLogger(CatalogSubscribeTask.class); - private Device device; - private ISIPCommander sipCommander; - - public CatalogSubscribeTask(Device device, ISIPCommander sipCommander) { - this.device = device; - this.sipCommander = sipCommander; - } - - @Override - public void run() { - sipCommander.catalogSubscribe(device, eventResult -> { - ResponseEvent event = (ResponseEvent) eventResult.event; - Element rootElement = null; - if (event.getResponse().getRawContent() != null) { - try { - rootElement = XmlUtil.getRootElement(event.getResponse().getRawContent(), "gb2312"); - } catch (DocumentException e) { - e.printStackTrace(); - } - Element resultElement = rootElement.element("Result"); - String result = resultElement.getText(); - if (result.toUpperCase().equals("OK")){ - // 成功 - logger.info("[目录订阅]成功: {}", device.getDeviceId()); - }else { - // 失败 - logger.info("[目录订阅]失败: {}-{}", device.getDeviceId(), result); - } - }else { - // 成功 - logger.info("[目录订阅]成功: {}", device.getDeviceId()); - } - },eventResult -> { - // 失败 - logger.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg); - }); - } -} 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 new file mode 100644 index 00000000..b814c18f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/GPSMsgInfo.java @@ -0,0 +1,106 @@ +package com.genersoft.iot.vmp.service.bean; + +public class GPSMsgInfo { + + /** + * + */ + private String id; + + /** + * 经度 (必选) + */ + private double lng; + + /** + * 纬度 (必选) + */ + private double lat; + + /** + * 速度,单位:km/h (可选) + */ + private double speed; + + /** + * 产生通知时间, 时间格式: 2020-01-14T14:32:12 + */ + private String time; + + /** + * 方向,取值为当前摄像头方向与正北方的顺时针夹角,取值范围0°~360°,单位:(°)(可选) + */ + private String direction; + + /** + * 海拔高度,单位:m(可选) + */ + private String altitude; + + private boolean stored; + + + 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/InviteTimeOutCallback.java b/src/main/java/com/genersoft/iot/vmp/service/bean/InviteTimeOutCallback.java new file mode 100644 index 00000000..e30db5d9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/InviteTimeOutCallback.java @@ -0,0 +1,6 @@ +package com.genersoft.iot.vmp.service.bean; + +public interface InviteTimeOutCallback { + + void run(int code, String msg); // code: 0 sip超时, 1 收流超时 +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/MediaServerLoad.java b/src/main/java/com/genersoft/iot/vmp/service/bean/MediaServerLoad.java new file mode 100644 index 00000000..cb30f67a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/MediaServerLoad.java @@ -0,0 +1,50 @@ +package com.genersoft.iot.vmp.service.bean; + +public class MediaServerLoad { + + private String id; + private int push; + private int proxy; + private int gbReceive; + private int gbSend; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getPush() { + return push; + } + + public void setPush(int push) { + this.push = push; + } + + public int getProxy() { + return proxy; + } + + public void setProxy(int proxy) { + this.proxy = proxy; + } + + public int getGbReceive() { + return gbReceive; + } + + public void setGbReceive(int gbReceive) { + this.gbReceive = gbReceive; + } + + public int getGbSend() { + return gbSend; + } + + public void setGbSend(int gbSend) { + this.gbSend = gbSend; + } +} 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 new file mode 100644 index 00000000..8491fc5e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannel.java @@ -0,0 +1,131 @@ +package com.genersoft.iot.vmp.service.bean; + +import java.util.stream.Stream; + +/** + * 当上级平台 + * @author lin + */ +public class MessageForPushChannel { + /** + * 消息类型 + * 0 流注销 1 流注册 + */ + private int type; + + /** + * 流应用名 + */ + private String app; + + /** + * 流Id + */ + private String stream; + + /** + * 国标ID + */ + private String gbId; + + /** + * 请求的平台ID + */ + private String platFormId; + + /** + * 请求平台名称 + */ + private String platFormName; + + /** + * WVP服务ID + */ + private String serverId; + + /** + * 目标流媒体节点ID + */ + private String mediaServerId; + + + + public static MessageForPushChannel getInstance(int type, String app, String stream, String gbId, + String platFormId, String platFormName, String serverId, + String mediaServerId){ + MessageForPushChannel messageForPushChannel = new MessageForPushChannel(); + messageForPushChannel.setType(type); + messageForPushChannel.setGbId(gbId); + messageForPushChannel.setApp(app); + messageForPushChannel.setStream(stream); + messageForPushChannel.setMediaServerId(mediaServerId); + messageForPushChannel.setPlatFormId(platFormId); + 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; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannelResponse.java b/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannelResponse.java new file mode 100644 index 00000000..10d1b43c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/MessageForPushChannelResponse.java @@ -0,0 +1,71 @@ +package com.genersoft.iot.vmp.service.bean; + +/** + * 当redis回复推流结果上级平台 + * @author lin + */ +public class MessageForPushChannelResponse { + /** + * 错误玛 + * 0 成功 1 失败 + */ + private int code; + /** + * 错误内容 + */ + private String msg; + + /** + * 流应用名 + */ + private String app; + + /** + * 流Id + */ + private String stream; + + + + public static MessageForPushChannelResponse getInstance(int code, String msg, String app, String stream){ + MessageForPushChannelResponse messageForPushChannel = new MessageForPushChannelResponse(); + messageForPushChannel.setCode(code); + messageForPushChannel.setMsg(msg); + messageForPushChannel.setApp(app); + messageForPushChannel.setStream(stream); + return messageForPushChannel; + } + + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + 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 getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackCallback.java b/src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackCallback.java new file mode 100644 index 00000000..33a09bd4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackCallback.java @@ -0,0 +1,7 @@ +package com.genersoft.iot.vmp.service.bean; + +public interface PlayBackCallback { + + void call(PlayBackResult msg); + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackResult.java b/src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackResult.java new file mode 100644 index 00000000..0a332ef0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/PlayBackResult.java @@ -0,0 +1,69 @@ +package com.genersoft.iot.vmp.service.bean; + +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; + +import java.util.EventObject; + + +/** + * @author lin + */ +public class PlayBackResult { + private int code; + + private String msg; + private T data; + private MediaServerItem mediaServerItem; + private JSONObject response; + private SipSubscribe.EventResult event; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public MediaServerItem getMediaServerItem() { + return mediaServerItem; + } + + public void setMediaServerItem(MediaServerItem mediaServerItem) { + this.mediaServerItem = mediaServerItem; + } + + public JSONObject getResponse() { + return response; + } + + public void setResponse(JSONObject response) { + this.response = response; + } + + public SipSubscribe.EventResult getEvent() { + return event; + } + + public void setEvent(SipSubscribe.EventResult event) { + this.event = event; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} 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 new file mode 100644 index 00000000..5bb7b77f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/PushStreamStatusChangeFromRedisDto.java @@ -0,0 +1,41 @@ +package com.genersoft.iot.vmp.service.bean; + +import java.util.List; + +/** + * 收到redis通知修改推流通道状态 + * @author lin + */ +public class PushStreamStatusChangeFromRedisDto { + + private boolean setAllOffline; + + 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/RequestPushStreamMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java new file mode 100644 index 00000000..5827d013 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestPushStreamMsg.java @@ -0,0 +1,170 @@ +package com.genersoft.iot.vmp.service.bean; + +/** + * redis消息:请求下级推送流信息 + * @author lin + */ +public class RequestPushStreamMsg { + + + /** + * 下级服务ID + */ + private String mediaServerId; + + /** + * 流ID + */ + private String app; + + /** + * 应用名 + */ + private String stream; + + /** + * 目标IP + */ + private String ip; + + /** + * 目标端口 + */ + private int port; + + /** + * ssrc + */ + private String ssrc; + + /** + * 是否使用TCP方式 + */ + private boolean tcp; + + /** + * 本地使用的端口 + */ + private int srcPort; + + /** + * 发送时,rtp的pt(uint8_t),不传时默认为96 + */ + private int pt; + + /** + * 发送时,rtp的负载类型。为true时,负载为ps;为false时,为es; + */ + private boolean ps; + + /** + * 是否只有音频 + */ + private boolean onlyAudio; + + + public static RequestPushStreamMsg getInstance(String mediaServerId, String app, String stream, String ip, int port, String ssrc, + boolean tcp, int srcPort, int pt, boolean ps, boolean onlyAudio) { + RequestPushStreamMsg requestPushStreamMsg = new RequestPushStreamMsg(); + requestPushStreamMsg.setMediaServerId(mediaServerId); + requestPushStreamMsg.setApp(app); + requestPushStreamMsg.setStream(stream); + requestPushStreamMsg.setIp(ip); + requestPushStreamMsg.setPort(port); + requestPushStreamMsg.setSsrc(ssrc); + requestPushStreamMsg.setTcp(tcp); + requestPushStreamMsg.setSrcPort(srcPort); + requestPushStreamMsg.setPt(pt); + requestPushStreamMsg.setPs(ps); + requestPushStreamMsg.setOnlyAudio(onlyAudio); + return requestPushStreamMsg; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + 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 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 boolean isTcp() { + return tcp; + } + + public void setTcp(boolean tcp) { + this.tcp = tcp; + } + + public int getSrcPort() { + return srcPort; + } + + public void setSrcPort(int srcPort) { + this.srcPort = srcPort; + } + + public int getPt() { + return pt; + } + + public void setPt(int pt) { + this.pt = pt; + } + + public boolean isPs() { + return ps; + } + + public void setPs(boolean ps) { + this.ps = ps; + } + + public boolean isOnlyAudio() { + return onlyAudio; + } + + public void setOnlyAudio(boolean onlyAudio) { + this.onlyAudio = onlyAudio; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/RequestSendItemMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestSendItemMsg.java new file mode 100644 index 00000000..0c6502d1 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/RequestSendItemMsg.java @@ -0,0 +1,188 @@ +package com.genersoft.iot.vmp.service.bean; + +/** + * redis消息:请求下级回复推送信息 + * @author lin + */ +public class RequestSendItemMsg { + + /** + * 下级服务ID + */ + private String serverId; + + /** + * 下级服务ID + */ + private String mediaServerId; + + /** + * 流ID + */ + private String app; + + /** + * 应用名 + */ + private String stream; + + /** + * 目标IP + */ + private String ip; + + /** + * 目标端口 + */ + private int port; + + /** + * ssrc + */ + private String ssrc; + + /** + * 平台国标编号 + */ + private String platformId; + + /** + * 平台名称 + */ + private String platformName; + + /** + * 通道ID + */ + private String channelId; + + + /** + * 是否使用TCP + */ + private Boolean isTcp; + + + /** + * 是否使用TCP + */ + private Boolean rtcp; + + + + + public static RequestSendItemMsg getInstance(String serverId, String mediaServerId, String app, String stream, String ip, int port, + String ssrc, String platformId, String channelId, Boolean isTcp, Boolean rtcp, String platformName) { + RequestSendItemMsg requestSendItemMsg = new RequestSendItemMsg(); + requestSendItemMsg.setServerId(serverId); + requestSendItemMsg.setMediaServerId(mediaServerId); + requestSendItemMsg.setApp(app); + requestSendItemMsg.setStream(stream); + requestSendItemMsg.setIp(ip); + requestSendItemMsg.setPort(port); + requestSendItemMsg.setSsrc(ssrc); + requestSendItemMsg.setPlatformId(platformId); + requestSendItemMsg.setPlatformName(platformName); + requestSendItemMsg.setChannelId(channelId); + requestSendItemMsg.setTcp(isTcp); + requestSendItemMsg.setRtcp(rtcp); + + return requestSendItemMsg; + } + + 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 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 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 getPlatformName() { + return platformName; + } + + public void setPlatformName(String platformName) { + this.platformName = platformName; + } + + public String getChannelId() { + return channelId; + } + + public void setChannelId(String channelId) { + this.channelId = channelId; + } + + public Boolean getTcp() { + return isTcp; + } + + public void setTcp(Boolean tcp) { + isTcp = tcp; + } + + public Boolean getRtcp() { + return rtcp; + } + + public void setRtcp(Boolean rtcp) { + this.rtcp = rtcp; + } +} 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 new file mode 100644 index 00000000..501621bb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/ResponseSendItemMsg.java @@ -0,0 +1,31 @@ +package com.genersoft.iot.vmp.service.bean; + +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; + +/** + * redis消息:下级回复推送信息 + * @author lin + */ +public class ResponseSendItemMsg { + + private SendRtpItem sendRtpItem; + + private MediaServerItem mediaServerItem; + + public SendRtpItem getSendRtpItem() { + return sendRtpItem; + } + + public void setSendRtpItem(SendRtpItem sendRtpItem) { + this.sendRtpItem = sendRtpItem; + } + + public MediaServerItem getMediaServerItem() { + return mediaServerItem; + } + + public void setMediaServerItem(MediaServerItem mediaServerItem) { + this.mediaServerItem = mediaServerItem; + } +} 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 faab1c80..1723bc59 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/SSRCInfo.java @@ -4,12 +4,12 @@ public class SSRCInfo { private int port; private String ssrc; - private String StreamId; + private String Stream; - public SSRCInfo(int port, String ssrc, String streamId) { + public SSRCInfo(int port, String ssrc, String stream) { this.port = port; this.ssrc = ssrc; - StreamId = streamId; + Stream = stream; } public int getPort() { @@ -28,11 +28,11 @@ public class SSRCInfo { this.ssrc = ssrc; } - public String getStreamId() { - return StreamId; + public String getStream() { + return Stream; } - public void setStreamId(String streamId) { - StreamId = streamId; + public void setStream(String stream) { + Stream = stream; } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/StreamPushItemFromRedis.java b/src/main/java/com/genersoft/iot/vmp/service/bean/StreamPushItemFromRedis.java new file mode 100644 index 00000000..ff32d791 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/StreamPushItemFromRedis.java @@ -0,0 +1,34 @@ +package com.genersoft.iot.vmp.service.bean; + + +public class StreamPushItemFromRedis { + private String app; + private String stream; + private long timeStamp; + + 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 getTimeStamp() { + return timeStamp; + } + + public void setTimeStamp(long timeStamp) { + this.timeStamp = timeStamp; + } +} + + diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java b/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java new file mode 100644 index 00000000..12d79cb2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsg.java @@ -0,0 +1,116 @@ +package com.genersoft.iot.vmp.service.bean; + +/** + * @author lin + */ +public class WvpRedisMsg { + + public static WvpRedisMsg getInstance(String fromId, String toId, String type, String cmd, String serial, String content){ + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); + wvpRedisMsg.setFromId(fromId); + wvpRedisMsg.setToId(toId); + wvpRedisMsg.setType(type); + wvpRedisMsg.setCmd(cmd); + wvpRedisMsg.setSerial(serial); + wvpRedisMsg.setContent(content); + return wvpRedisMsg; + } + + private String fromId; + + private String toId; + /** + * req 请求, res 回复 + */ + private String type; + private String cmd; + + /** + * 消息的ID + */ + private String serial; + private Object content; + + private final static String requestTag = "req"; + private final static String responseTag = "res"; + + public static WvpRedisMsg getRequestInstance(String fromId, String toId, String cmd, String serial, Object content) { + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); + wvpRedisMsg.setType(requestTag); + wvpRedisMsg.setFromId(fromId); + wvpRedisMsg.setToId(toId); + wvpRedisMsg.setCmd(cmd); + wvpRedisMsg.setSerial(serial); + wvpRedisMsg.setContent(content); + return wvpRedisMsg; + } + + public static WvpRedisMsg getResponseInstance() { + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); + wvpRedisMsg.setType(responseTag); + return wvpRedisMsg; + } + + public static WvpRedisMsg getResponseInstance(String fromId, String toId, String cmd, String serial, Object content) { + WvpRedisMsg wvpRedisMsg = new WvpRedisMsg(); + wvpRedisMsg.setType(responseTag); + wvpRedisMsg.setFromId(fromId); + wvpRedisMsg.setToId(toId); + wvpRedisMsg.setCmd(cmd); + wvpRedisMsg.setSerial(serial); + wvpRedisMsg.setContent(content); + return wvpRedisMsg; + } + + public static boolean isRequest(WvpRedisMsg wvpRedisMsg) { + return requestTag.equals(wvpRedisMsg.getType()); + } + + public String getSerial() { + return serial; + } + + public void setSerial(String serial) { + this.serial = serial; + } + + public String getFromId() { + return fromId; + } + + public void setFromId(String fromId) { + this.fromId = fromId; + } + + public String getToId() { + return toId; + } + + public void setToId(String toId) { + this.toId = toId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getCmd() { + return cmd; + } + + public void setCmd(String cmd) { + this.cmd = cmd; + } + + public Object getContent() { + return content; + } + + public void setContent(Object content) { + this.content = content; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsgCmd.java b/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsgCmd.java new file mode 100644 index 00000000..cb118865 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/WvpRedisMsgCmd.java @@ -0,0 +1,12 @@ +package com.genersoft.iot.vmp.service.bean; + +/** + * @author lin + */ + +public class WvpRedisMsgCmd { + + public static final String GET_SEND_ITEM = "GetSendItem"; + public static final String REQUEST_PUSH_STREAM = "RequestPushStream"; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java index ead291c0..8c55986e 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImpl.java @@ -17,7 +17,6 @@ public class DeviceAlarmServiceImpl implements IDeviceAlarmService { @Autowired private DeviceAlarmMapper deviceAlarmMapper; - @Override public PageInfo getAllAlarm(int page, int count, String deviceId, String alarmPriority, String alarmMethod, String alarmType, String startTime, String endTime) { PageHelper.startPage(page, count); 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 new file mode 100644 index 00000000..880b6971 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceChannelServiceImpl.java @@ -0,0 +1,180 @@ +package com.genersoft.iot.vmp.service.impl; + +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.utils.Coordtransform; +import com.genersoft.iot.vmp.service.IDeviceChannelService; +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.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; +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 java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * @author lin + */ +@Service +public class DeviceChannelServiceImpl implements IDeviceChannelService { + + private final static Logger logger = LoggerFactory.getLogger(DeviceChannelServiceImpl.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private DeviceChannelMapper channelMapper; + + @Autowired + private DeviceMapper deviceMapper; + + @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); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); + if (streamInfo != null) { + channel.setStreamId(streamInfo.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); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId()); + if (streamInfo != null) { + channel.setStreamId(streamInfo.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); + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channel.getChannelId()); + if (streamInfo != null) { + channel.setStreamId(streamInfo.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 = 300; + 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 ResourceBaceInfo getOverview() { + return channelMapper.getOverview(); + } + + + @Override + public List queryAllChannelList(String platformId) { + return channelMapper.queryChannelListInAll(null, null, null, platformId, null); + } + + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java index 11594847..7bb9619b 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java @@ -1,14 +1,42 @@ package com.genersoft.iot.vmp.service.impl; +import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; -import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; +import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; +import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; +import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; +import com.genersoft.iot.vmp.service.IDeviceChannelService; import com.genersoft.iot.vmp.service.IDeviceService; -import com.genersoft.iot.vmp.service.bean.CatalogSubscribeTask; +import com.genersoft.iot.vmp.service.IMediaServerService; +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.ResourceBaceInfo; 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 javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; /** * 设备业务(目录订阅) @@ -20,25 +48,157 @@ public class DeviceServiceImpl implements IDeviceService { @Autowired private DynamicTask dynamicTask; -; @Autowired private ISIPCommander sipCommander; + @Autowired + private CatalogResponseMessageHandler catalogResponseMessageHandler; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private DeviceMapper deviceMapper; + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private DeviceChannelMapper deviceChannelMapper; + + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + + @Autowired + private UserSetting userSetting; + + @Autowired + private ISIPCommander commander; + + @Autowired + private VideoStreamSessionManager streamSession; + + @Autowired + private IMediaServerService mediaServerService; + + @Override + public void online(Device device) { + logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort()); + Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId()); + Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); + + String now = DateUtil.getNow(); + if (deviceInRedis != null && deviceInDb == null) { + // redis 存在脏数据 + redisCatchStorage.clearCatchByDeviceId(device.getDeviceId()); + } + device.setUpdateTime(now); + if (device.getKeepaliveIntervalTime() == 0) { + // 默认心跳间隔60 + device.setKeepaliveIntervalTime(60); + } + // 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询 + if (device.getCreateTime() == null) { + device.setOnline(1); + device.setCreateTime(now); + logger.info("[设备上线,首次注册]: {},查询设备信息以及通道信息", device.getDeviceId()); + deviceMapper.add(device); + redisCatchStorage.updateDevice(device); + try { + commander.deviceInfoQuery(device); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); + } + sync(device); + }else { + if(device.getOnline() == 0){ + device.setOnline(1); + device.setCreateTime(now); + deviceMapper.update(device); + redisCatchStorage.updateDevice(device); + if (userSetting.getSyncChannelOnDeviceOnline()) { + logger.info("[设备上线,离线状态下重新注册]: {},查询设备信息以及通道信息", device.getDeviceId()); + try { + commander.deviceInfoQuery(device); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 查询设备信息: {}", e.getMessage()); + } + sync(device); + // TODO 如果设备下的通道级联到了其他平台,那么需要发送事件或者notify给上级平台 + } + }else { + if (deviceChannelMapper.queryAllChannels(device.getDeviceId()).size() == 0) { + logger.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId()); + sync(device); + } + + deviceMapper.update(device); + redisCatchStorage.updateDevice(device); + } + + } + + // 上线添加订阅 + if (device.getSubscribeCycleForCatalog() > 0) { + // 查询在线设备那些开启了订阅,为设备开启定时的目录订阅 + addCatalogSubscribe(device); + } + if (device.getSubscribeCycleForMobilePosition() > 0) { + addMobilePositionSubscribe(device); + } + // 刷新过期任务 + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); + // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线 + dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getKeepaliveIntervalTime() * 1000 * 3); + } + + @Override + public void offline(String deviceId) { + logger.error("[设备离线], device:{}", deviceId); + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + return; + } + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + deviceId; + dynamicTask.stop(registerExpireTaskKey); + device.setOnline(0); + redisCatchStorage.updateDevice(device); + deviceMapper.update(device); + //进行通道离线 +// deviceChannelMapper.offlineByDeviceId(deviceId); + // 离线释放所有ssrc + List ssrcTransactions = streamSession.getSsrcTransactionForAll(deviceId, null, null, null); + if (ssrcTransactions != null && ssrcTransactions.size() > 0) { + for (SsrcTransaction ssrcTransaction : ssrcTransactions) { + mediaServerService.releaseSsrc(ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); + mediaServerService.closeRTPServer(ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); + streamSession.remove(deviceId, ssrcTransaction.getChannelId(), ssrcTransaction.getStream()); + } + } + // 移除订阅 + removeCatalogSubscribe(device); + removeMobilePositionSubscribe(device); + } + @Override public boolean addCatalogSubscribe(Device device) { if (device == null || device.getSubscribeCycleForCatalog() < 0) { return false; } + logger.info("[添加目录订阅] 设备{}", device.getDeviceId()); // 添加目录订阅 - CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander); - catalogSubscribeTask.run(); - // 提前开始刷新订阅 - // TODO 使用jain sip的当时刷新订阅 - int subscribeCycleForCatalog = device.getSubscribeCycleForCatalog(); + CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander, dynamicTask); + // 刷新订阅 + int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForCatalog(),30); // 设置最小值为30 - subscribeCycleForCatalog = Math.max(subscribeCycleForCatalog, 30); - dynamicTask.startCron(device.getDeviceId(), catalogSubscribeTask, subscribeCycleForCatalog - 5); + dynamicTask.startCron(device.getDeviceId() + "catalog", catalogSubscribeTask, (subscribeCycleForCatalog -1) * 1000); return true; } @@ -47,12 +207,428 @@ public class DeviceServiceImpl implements IDeviceService { if (device == null || device.getSubscribeCycleForCatalog() < 0) { return false; } - logger.info("移除目录订阅: {}", device.getDeviceId()); - dynamicTask.stopCron(device.getDeviceId()); - device.setSubscribeCycleForCatalog(0); - sipCommander.catalogSubscribe(device, null, null); - // 清空cseq计数 - + logger.info("[移除目录订阅]: {}", device.getDeviceId()); + String taskKey = device.getDeviceId() + "catalog"; + if (device.getOnline() == 1) { + Runnable runnable = dynamicTask.get(taskKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + } + dynamicTask.stop(taskKey); return true; } + + @Override + public boolean addMobilePositionSubscribe(Device device) { + if (device == null || device.getSubscribeCycleForMobilePosition() < 0) { + return false; + } + logger.info("[添加移动位置订阅] 设备{}", device.getDeviceId()); + // 添加目录订阅 + MobilePositionSubscribeTask mobilePositionSubscribeTask = new MobilePositionSubscribeTask(device, sipCommander, dynamicTask); + // 设置最小值为30 + int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForMobilePosition(),30); + // 刷新订阅 + dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, (subscribeCycleForCatalog) * 1000); + return true; + } + + @Override + public boolean removeMobilePositionSubscribe(Device device) { + if (device == null || device.getSubscribeCycleForCatalog() < 0) { + return false; + } + logger.info("[移除移动位置订阅]: {}", device.getDeviceId()); + String taskKey = device.getDeviceId() + "mobile_position"; + if (device.getOnline() == 1) { + Runnable runnable = dynamicTask.get(taskKey); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + } + dynamicTask.stop(taskKey); + return true; + } + + @Override + public SyncStatus getChannelSyncStatus(String deviceId) { + return catalogResponseMessageHandler.getChannelSyncProgress(deviceId); + } + + @Override + public Boolean isSyncRunning(String deviceId) { + return catalogResponseMessageHandler.isSyncRunning(deviceId); + } + + @Override + public void sync(Device device) { + if (catalogResponseMessageHandler.isSyncRunning(device.getDeviceId())) { + logger.info("开启同步时发现同步已经存在"); + return; + } + int sn = (int)((Math.random()*9+1)*100000); + catalogResponseMessageHandler.setChannelSyncReady(device, sn); + try { + sipCommander.catalogQuery(device, sn, event -> { + String errorMsg = String.format("同步通道失败,错误码: %s, %s", event.statusCode, event.msg); + catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg); + }); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[同步通道], 信令发送失败:{}", e.getMessage() ); + String errorMsg = String.format("同步通道失败,信令发送失败: %s", e.getMessage()); + catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg); + } + } + + @Override + public Device getDevice(String deviceId) { + Device device = redisCatchStorage.getDevice(deviceId); + if (device == null) { + device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device != null) { + redisCatchStorage.updateDevice(device); + } + } + return device; + } + + @Override + public List getAllOnlineDevice() { + return deviceMapper.getOnlineDevices(); + } + + @Override + public boolean expire(Device device) { + Instant registerTimeDate = Instant.from(DateUtil.formatter.parse(device.getRegisterTime())); + Instant expireInstant = registerTimeDate.plusMillis(TimeUnit.SECONDS.toMillis(device.getExpires())); + return expireInstant.isBefore(Instant.now()); + } + + @Override + public void checkDeviceStatus(Device device) { + if (device == null || device.getOnline() == 0) { + return; + } + try { + sipCommander.deviceStatusQuery(device, null); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 设备状态查询: {}", e.getMessage()); + } + + } + + @Override + public Device getDeviceByHostAndPort(String host, int port) { + return deviceMapper.getDeviceByHostAndPort(host, port); + } + + @Override + public void updateDevice(Device device) { + + String now = DateUtil.getNow(); + device.setUpdateTime(now); + device.setCharset(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 (parentId == null || parentId.equals(deviceId)) { + // 字根节点开始查询 + List rootNodes = getRootNodes(deviceId, TreeType.CIVIL_CODE.equals(device.getTreeType()), true, !onlyCatalog); + return transportChannelsToTree(rootNodes, ""); + } + + if (TreeType.CIVIL_CODE.equals(device.getTreeType())) { + if (parentId.length()%2 != 0) { + return null; + } + // 使用行政区划展示树 +// if (parentId.length() > 10) { +// // TODO 可能是行政区划与业务分组混杂的情形 +// return null; +// } + + if (parentId.length() == 10 ) { + if (onlyCatalog) { + return null; + } + // parentId为行业编码, 其下不会再有行政区划 + List channels = deviceChannelMapper.getChannelsByCivilCode(deviceId, parentId); + List> trees = transportChannelsToTree(channels, parentId); + return trees; + } + // 查询其下的行政区划和摄像机 + List channelsForCivilCode = deviceChannelMapper.getChannelsWithCivilCodeAndLength(deviceId, parentId, parentId.length() + 2); + if (!onlyCatalog) { + List channels = deviceChannelMapper.getChannelsByCivilCode(deviceId, parentId); + + for(DeviceChannel channel : channels) { + boolean flag = false; + for(DeviceChannel deviceChannel : channelsForCivilCode) { + if(channel.getChannelId().equals(deviceChannel.getChannelId())) { + flag = true; + } + } + if(!flag) { + channelsForCivilCode.add(channel); + } + } + } + List> trees = transportChannelsToTree(channelsForCivilCode, parentId); + return trees; + + } + // 使用业务分组展示树 + if (TreeType.BUSINESS_GROUP.equals(device.getTreeType())) { + if (parentId.length() < 14 ) { + return null; + } + List deviceChannels = deviceChannelMapper.queryChannels(deviceId, parentId, null, null, null,null); + List> trees = transportChannelsToTree(deviceChannels, parentId); + return trees; + } + + return null; + } + + @Override + public List queryVideoDeviceInTreeNode(String deviceId, String parentId) { + Device device = deviceMapper.getDeviceByDeviceId(deviceId); + if (device == null) { + return null; + } + if (parentId == null || parentId.equals(deviceId)) { + // 字根节点开始查询 + List rootNodes = getRootNodes(deviceId, TreeType.CIVIL_CODE.equals(device.getTreeType()), false, true); + return rootNodes; + } + + if (TreeType.CIVIL_CODE.equals(device.getTreeType())) { + if (parentId.length()%2 != 0) { + return null; + } + // 使用行政区划展示树 + if (parentId.length() > 10) { + // TODO 可能是行政区划与业务分组混杂的情形 + return null; + } + + if (parentId.length() == 10 ) { + // parentId为行业编码, 其下不会再有行政区划 + List channels = deviceChannelMapper.getChannelsByCivilCode(deviceId, parentId); + return channels; + } + // 查询其下的行政区划和摄像机 + List channels = deviceChannelMapper.getChannelsByCivilCode(deviceId, parentId); + return channels; + + } + // 使用业务分组展示树 + if (TreeType.BUSINESS_GROUP.equals(device.getTreeType())) { + if (parentId.length() < 14 ) { + return null; + } + List deviceChannels = deviceChannelMapper.queryChannels(deviceId, parentId, null, null, null,null); + return deviceChannels; + } + + return null; + } + + 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) { + String gbCodeType = channel.getChannelId().substring(10, 13); + node.setParent(gbCodeType.equals(ChannelIdType.BUSINESS_GROUP) || gbCodeType.equals(ChannelIdType.VIRTUAL_ORGANIZATION) ); + }else { + node.setParent(true); + } + treeNotes.add(node); + } + Collections.sort(treeNotes); + return treeNotes; + } + + private List getRootNodes(String deviceId, boolean isCivilCode, boolean haveCatalog, boolean haveChannel) { + if (!haveCatalog && !haveChannel) { + return null; + } + List result = new ArrayList<>(); + if (isCivilCode) { + // 使用行政区划 + Integer length= deviceChannelMapper.getChannelMinLength(deviceId); + if (length == null) { + return null; + } + if (length <= 10) { + if (haveCatalog) { + List provinceNode = deviceChannelMapper.getChannelsWithCivilCodeAndLength(deviceId, null, length); + if (provinceNode != null && provinceNode.size() > 0) { + result.addAll(provinceNode); + } + } + + if (haveChannel) { + // 查询那些civilCode不在通道中的不规范通道,放置在根目录 + List nonstandardNode = deviceChannelMapper.getChannelWithoutCiviCode(deviceId); + if (nonstandardNode != null && nonstandardNode.size() > 0) { + result.addAll(nonstandardNode); + } + } + }else { + if (haveChannel) { + List deviceChannels = deviceChannelMapper.queryChannels(deviceId, null, null, null, null,null); + if (deviceChannels != null && deviceChannels.size() > 0) { + result.addAll(deviceChannels); + } + } + } + + }else { + // 使用业务分组+虚拟组织 + + // 只获取业务分组 + List deviceChannels = deviceChannelMapper.getBusinessGroups(deviceId, ChannelIdType.BUSINESS_GROUP); + if (deviceChannels != null && deviceChannels.size() > 0) { + result.addAll(deviceChannels); + } + } + return result; + } + + @Override + public boolean isExist(String deviceId) { + return deviceMapper.getDeviceByDeviceId(deviceId) != null; + } + + @Override + public void addDevice(Device device) { + device.setOnline(0); + device.setCreateTime(DateUtil.getNow()); + device.setUpdateTime(DateUtil.getNow()); + deviceMapper.addCustomDevice(device); + } + + @Override + public void updateCustomDevice(Device device) { + Device deviceInStore = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); + if (deviceInStore == null) { + logger.warn("更新设备时未找到设备信息"); + return; + } + if (!ObjectUtils.isEmpty(device.getName())) { + deviceInStore.setName(device.getName()); + } + if (!ObjectUtils.isEmpty(device.getCharset())) { + deviceInStore.setCharset(device.getCharset()); + } + if (!ObjectUtils.isEmpty(device.getMediaServerId())) { + deviceInStore.setMediaServerId(device.getMediaServerId()); + } + deviceInStore.setSdpIp(device.getSdpIp()); + deviceInStore.setCharset(device.getCharset()); + deviceInStore.setTreeType(device.getTreeType()); + + // 目录订阅相关的信息 + if (device.getSubscribeCycleForCatalog() > 0) { + if (deviceInStore.getSubscribeCycleForCatalog() == 0 || deviceInStore.getSubscribeCycleForCatalog() != device.getSubscribeCycleForCatalog()) { + deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); + // 开启订阅 + addCatalogSubscribe(deviceInStore); + } + }else if (device.getSubscribeCycleForCatalog() == 0) { + if (deviceInStore.getSubscribeCycleForCatalog() != 0) { + deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); + // 取消订阅 + removeCatalogSubscribe(deviceInStore); + } + } + + // 移动位置订阅相关的信息 + if (device.getSubscribeCycleForMobilePosition() > 0) { + if (deviceInStore.getSubscribeCycleForMobilePosition() == 0 || deviceInStore.getSubscribeCycleForMobilePosition() != device.getSubscribeCycleForMobilePosition()) { + deviceInStore.setMobilePositionSubmissionInterval(device.getMobilePositionSubmissionInterval()); + deviceInStore.setSubscribeCycleForMobilePosition(device.getSubscribeCycleForMobilePosition()); + // 开启订阅 + addMobilePositionSubscribe(deviceInStore); + } + }else if (device.getSubscribeCycleForMobilePosition() == 0) { + if (deviceInStore.getSubscribeCycleForMobilePosition() != 0) { + // 取消订阅 + removeMobilePositionSubscribe(deviceInStore); + } + } + // 坐标系变化,需要重新计算GCJ02坐标和WGS84坐标 + if (!deviceInStore.getGeoCoordSys().equals(device.getGeoCoordSys())) { + updateDeviceChannelGeoCoordSys(device); + } + // 更新redis + redisCatchStorage.updateDevice(device); + deviceMapper.updateCustom(device); + } + + @Override + 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); + } + return result; + } + + @Override + public ResourceBaceInfo getOverview() { + return deviceMapper.getOverview(); + } } 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 index 5002d29d..89acb065 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java @@ -1,9 +1,14 @@ package com.genersoft.iot.vmp.service.impl; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; -import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; +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; @@ -13,7 +18,9 @@ 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; @Service @@ -33,10 +40,19 @@ public class GbStreamServiceImpl implements IGbStreamService { @Autowired private PlatformGbStreamMapper platformGbStreamMapper; + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + private PlatformCatalogMapper catalogMapper; + + @Autowired + private EventPublisher eventPublisher; + @Override - public PageInfo getAll(Integer page, Integer count) { + public PageInfo getAll(Integer page, Integer count, String platFormId, String catalogId, String query, String mediaServerId) { PageHelper.startPage(page, count); - List all = gbStreamMapper.selectAll(); + List all = gbStreamMapper.selectAll(platFormId, catalogId, query, mediaServerId); return new PageInfo<>(all); } @@ -47,34 +63,84 @@ public class GbStreamServiceImpl implements IGbStreamService { @Override - public boolean addPlatformInfo(List gbStreams, String platformId) { + 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 (GbStream gbStream : gbStreams) { + gbStream.setCatalogId(catalogId); gbStream.setPlatformId(platformId); + // TODO 修改为批量提交 platformGbStreamMapper.add(gbStream); + DeviceChannel deviceChannelListByStream = getDeviceChannelListByStreamWithStatus(gbStream, catalogId, parentPlatform); + deviceChannelList.add(deviceChannelListByStream); } dataSourceTransactionManager.commit(transactionStatus); //手动提交 + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); result = true; }catch (Exception e) { logger.error("批量保存流与平台的关系时错误", e); dataSourceTransactionManager.rollback(transactionStatus); } return result; - } @Override - public boolean delPlatformInfo(List gbStreams) { + 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()?1:0); + + deviceChannel.setRegisterWay(1); + deviceChannel.setCivilCode(platform.getAdministrativeDivision()); + + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)){ + deviceChannel.setCivilCode(catalogId); + }else if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)){ + PlatformCatalog catalog = catalogMapper.select(catalogId); + if (catalog == null) { + deviceChannel.setParentId(platform.getDeviceGBId()); + deviceChannel.setBusinessGroupId(null); + }else { + deviceChannel.setParentId(catalog.getId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + } + + } + + 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) { - platformGbStreamMapper.delByAppAndStream(gbStream.getApp(), gbStream.getStream()); + 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) { @@ -83,4 +149,119 @@ public class GbStreamServiceImpl implements IGbStreamService { } 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 )?1:0); + + deviceChannel.setRegisterWay(1); + deviceChannel.setCivilCode(platform.getAdministrativeDivision()); + + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)){ + deviceChannel.setCivilCode(catalogId); + }else if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)){ + PlatformCatalog catalog = catalogMapper.select(catalogId); + if (catalog == null) { + deviceChannel.setParentId(platform.getDeviceGBId()); + deviceChannel.setBusinessGroupId(null); + }else { + deviceChannel.setParentId(catalog.getId()); + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + } + + } + + 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 = platform.getDeviceGBId(); + } + if (platformGbStreamMapper.delByPlatformAndCatalogId(platformId, catalogId) > 0) { + 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); + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java index 5b170681..64105a63 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java @@ -1,12 +1,13 @@ package com.genersoft.iot.vmp.service.impl; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +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.SipConfig; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.gb28181.session.SsrcConfig; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; @@ -14,35 +15,41 @@ import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData; import com.genersoft.iot.vmp.service.IMediaServerService; +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.utils.redis.JedisUtil; +import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.redis.RedisUtil; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import okhttp3.*; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +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.beans.factory.annotation.Value; -import org.springframework.boot.CommandLineRunner; -import org.springframework.core.annotation.Order; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; -import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.LocalDateTime; import java.util.*; /** * 媒体服务器节点管理 */ @Service -@Order(value=2) -public class MediaServerServiceImpl implements IMediaServerService, CommandLineRunner { +public class MediaServerServiceImpl implements IMediaServerService { private final static Logger logger = LoggerFactory.getLogger(MediaServerServiceImpl.class); + private final String zlmKeepaliveKeyPrefix = "zlm-keepalive_"; + @Autowired private SipConfig sipConfig; @@ -53,7 +60,7 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR private Integer serverPort; @Autowired - private UserSetup userSetup; + private UserSetting userSetting; @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @@ -61,108 +68,126 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR @Autowired private MediaServerMapper mediaServerMapper; + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + @Autowired private VideoStreamSessionManager streamSession; @Autowired private ZLMRTPServerFactory zlmrtpServerFactory; - @Autowired - private RedisUtil redisUtil; - @Autowired private EventPublisher publisher; @Autowired - JedisUtil jedisUtil; + private DynamicTask dynamicTask; - private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + @Autowired + private IRedisCatchStorage redisCatchStorage; /** * 初始化 */ @Override - public void run(String... args) throws Exception { - logger.info("[缓存初始化] Media Server "); - List mediaServerItemList = mediaServerMapper.queryAll(); + public void updateVmServer(List mediaServerItemList) { + logger.info("[zlm] 缓存初始化 "); for (MediaServerItem mediaServerItem : mediaServerItemList) { - if (StringUtils.isEmpty(mediaServerItem.getId())) { + if (ObjectUtils.isEmpty(mediaServerItem.getId())) { continue; } // 更新 if (mediaServerItem.getSsrcConfig() == null) { SsrcConfig ssrcConfig = new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain()); mediaServerItem.setSsrcConfig(ssrcConfig); - redisUtil.set(VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + mediaServerItem.getId(), mediaServerItem); + RedisUtil.set(VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(), mediaServerItem); } // 查询redis是否存在此mediaServer - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + mediaServerItem.getId(); - if (!redisUtil.hasKey(key)) { - redisUtil.set(key, mediaServerItem); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); + if (!RedisUtil.hasKey(key)) { + RedisUtil.set(key, mediaServerItem); } } } @Override - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId) { - return openRTPServer(mediaServerItem, streamId, false); + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean ssrcCheck, boolean isPlayback) { + return openRTPServer(mediaServerItem, streamId, null, ssrcCheck,isPlayback); } @Override - public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, boolean isPlayback) { + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String presetSsrc, boolean ssrcCheck, boolean isPlayback, Integer port) { if (mediaServerItem == null || mediaServerItem.getId() == null) { + logger.info("[openRTPServer] 失败, mediaServerItem == null || mediaServerItem.getId() == null"); return null; } // 获取mediaServer可用的ssrc - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + mediaServerItem.getId(); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig(); if (ssrcConfig == null) { logger.info("media server [ {} ] ssrcConfig is null", mediaServerItem.getId()); return null; }else { - String ssrc = null; - if (isPlayback) { - ssrc = ssrcConfig.getPlayBackSsrc(); + String ssrc; + if (presetSsrc != null) { + ssrc = presetSsrc; }else { - ssrc = ssrcConfig.getPlaySsrc(); + if (isPlayback) { + ssrc = ssrcConfig.getPlayBackSsrc(); + }else { + ssrc = ssrcConfig.getPlaySsrc(); + } } if (streamId == null) { streamId = String.format("%08x", Integer.parseInt(ssrc)).toUpperCase(); } - int rtpServerPort = mediaServerItem.getRtpProxyPort(); + int rtpServerPort; if (mediaServerItem.isRtpEnable()) { - rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId); + rtpServerPort = zlmrtpServerFactory.createRTPServer(mediaServerItem, streamId, ssrcCheck?Integer.parseInt(ssrc):0, port); + } else { + rtpServerPort = mediaServerItem.getRtpProxyPort(); } - redisUtil.set(key, mediaServerItem); + RedisUtil.set(key, mediaServerItem); return new SSRCInfo(rtpServerPort, ssrc, streamId); } } @Override - public void closeRTPServer(Device device, String channelId) { - String mediaServerId = streamSession.getMediaServerId(device.getDeviceId(), channelId); - MediaServerItem mediaServerItem = this.getOne(mediaServerId); - if (mediaServerItem != null) { - String streamId = String.format("%s_%s", device.getDeviceId(), channelId); - zlmrtpServerFactory.closeRTPServer(mediaServerItem, streamId); - releaseSsrc(mediaServerItem, streamSession.getSSRC(device.getDeviceId(), channelId)); - } - streamSession.remove(device.getDeviceId(), channelId); + public SSRCInfo openRTPServer(MediaServerItem mediaServerItem, String streamId, String ssrc, boolean ssrcCheck, boolean isPlayback) { + return openRTPServer(mediaServerItem, streamId, ssrc, ssrcCheck, isPlayback, null); } @Override - public void releaseSsrc(MediaServerItem mediaServerItem, String ssrc) { + public void closeRTPServer(MediaServerItem mediaServerItem, String streamId) { + if (mediaServerItem == null) { + return; + } + zlmrtpServerFactory.closeRtpServer(mediaServerItem, streamId); + } + + @Override + public void closeRTPServer(String mediaServerId, String streamId) { + MediaServerItem mediaServerItem = this.getOne(mediaServerId); + closeRTPServer(mediaServerItem, streamId); + } + + @Override + public void releaseSsrc(String mediaServerItemId, String ssrc) { + MediaServerItem mediaServerItem = getOne(mediaServerItemId); if (mediaServerItem == null || ssrc == null) { return; } SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig(); ssrcConfig.releaseSsrc(ssrc); mediaServerItem.setSsrcConfig(ssrcConfig); - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + mediaServerItem.getId(); - redisUtil.set(key, mediaServerItem); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); + RedisUtil.set(key, mediaServerItem); } /** @@ -171,7 +196,8 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR @Override public void clearRTPServer(MediaServerItem mediaServerItem) { mediaServerItem.setSsrcConfig(new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain())); - redisUtil.zAdd(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetup.getServerId(), mediaServerItem.getId(), 0); + RedisUtil.zAdd(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), mediaServerItem.getId(), 0); + } @@ -191,31 +217,31 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR ) ); } - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + mediaServerItemInDataBase.getId(); - redisUtil.set(key, mediaServerItemInDataBase); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItemInDataBase.getId(); + RedisUtil.set(key, mediaServerItemInDataBase); } @Override public List getAll() { List result = new ArrayList<>(); - List mediaServerKeys = redisUtil.scan(String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX+ userSetup.getServerId() + "_" )); - String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetup.getServerId(); + List mediaServerKeys = RedisUtil.scan(String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX+ userSetting.getServerId() + "_" )); + String onlineKey = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); for (Object mediaServerKey : mediaServerKeys) { String key = (String) mediaServerKey; - MediaServerItem mediaServerItem = (MediaServerItem) redisUtil.get(key); + MediaServerItem mediaServerItem = (MediaServerItem) RedisUtil.get(key); // 检查状态 - if (redisUtil.zScore(onlineKey, mediaServerItem.getId()) != null) { + Double aDouble = RedisUtil.zScore(onlineKey, mediaServerItem.getId()); + if (aDouble != null) { mediaServerItem.setStatus(true); } result.add(mediaServerItem); } result.sort((serverItem1, serverItem2)->{ int sortResult = 0; - try { - sortResult = format.parse(serverItem1.getCreateTime()).compareTo(format.parse(serverItem2.getCreateTime())); - } catch (ParseException e) { - e.printStackTrace(); - } + LocalDateTime localDateTime1 = LocalDateTime.parse(serverItem1.getCreateTime(), DateUtil.formatter); + LocalDateTime localDateTime2 = LocalDateTime.parse(serverItem2.getCreateTime(), DateUtil.formatter); + + sortResult = localDateTime1.compareTo(localDateTime2); return sortResult; }); return result; @@ -229,15 +255,17 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR @Override public List getAllOnline() { - String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetup.getServerId(); - Set mediaServerIdSet = redisUtil.zRevRange(key, 0, -1); + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + Set mediaServerIdSet = RedisUtil.zRevRange(key, 0, -1); + List result = new ArrayList<>(); if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) { for (String mediaServerId : mediaServerIdSet) { - String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + mediaServerId; - result.add((MediaServerItem) redisUtil.get(serverKey)); + String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId; + result.add((MediaServerItem) RedisUtil.get(serverKey)); } } + Collections.reverse(result); return result; } @@ -251,58 +279,46 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR if (mediaServerId == null) { return null; } - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + mediaServerId; - return (MediaServerItem)redisUtil.get(key); - } - - @Override - public MediaServerItem getOneByHostAndPort(String host, int port) { - return mediaServerMapper.queryOneByHostAndPort(host, port); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerId; + return (MediaServerItem)RedisUtil.get(key); } @Override public MediaServerItem getDefaultMediaServer() { + return mediaServerMapper.queryDefault(); } @Override public void clearMediaServerForOnline() { - String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetup.getServerId(); - redisUtil.del(key); + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + RedisUtil.del(key); } @Override - public WVPResult add(MediaServerItem mediaServerItem) { - WVPResult result = new WVPResult<>(); - mediaServerItem.setCreateTime(this.format.format(System.currentTimeMillis())); - mediaServerItem.setUpdateTime(this.format.format(System.currentTimeMillis())); - mediaServerItem.setHookAliveInterval(120); + public void add(MediaServerItem mediaServerItem) { + mediaServerItem.setCreateTime(DateUtil.getNow()); + mediaServerItem.setUpdateTime(DateUtil.getNow()); + mediaServerItem.setHookAliveInterval(30f); JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); if (responseJSON != null) { JSONArray data = responseJSON.getJSONArray("data"); if (data != null && data.size() > 0) { ZLMServerConfig zlmServerConfig= JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); if (mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()) != null) { - result.setCode(-1); - result.setMsg("保存失败,媒体服务ID [ " + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置"); - return result; + throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败,媒体服务ID [ " + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置"); } mediaServerItem.setId(zlmServerConfig.getGeneralMediaServerId()); zlmServerConfig.setIp(mediaServerItem.getIp()); mediaServerMapper.add(mediaServerItem); zlmServerOnline(zlmServerConfig); - result.setCode(0); - result.setMsg("success"); }else { - result.setCode(-1); - result.setMsg("连接失败"); + throw new ControllerException(ErrorCode.ERROR100.getCode(),"连接失败"); } }else { - result.setCode(-1); - result.setMsg("连接失败"); + throw new ControllerException(ErrorCode.ERROR100.getCode(),"连接失败"); } - return result; } @Override @@ -312,7 +328,22 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR @Override public int updateToDatabase(MediaServerItem mediaSerItem) { - return mediaServerMapper.update(mediaSerItem); + int result = 0; + if (mediaSerItem.isDefaultServer()) { + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + int delResult = mediaServerMapper.delDefault(); + if (delResult == 0) { + logger.error("移除数据库默认zlm节点失败"); + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + return 0; + } + result = mediaServerMapper.add(mediaSerItem); + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + }else { + result = mediaServerMapper.update(mediaSerItem); + } + return result; } /** @@ -321,16 +352,15 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR */ @Override public void zlmServerOnline(ZLMServerConfig zlmServerConfig) { - logger.info("[ ZLM:{} ]-[ {}:{} ]已连接", - zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); MediaServerItem serverItem = mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()); if (serverItem == null) { - serverItem = mediaServerMapper.queryOneByHostAndPort(zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); - } - if (serverItem == null) { - logger.warn("[未注册的zlm] 拒接接入:来自{}:{}", zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); + logger.warn("[未注册的zlm] 拒接接入:{}来自{}:{}", zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); + logger.warn("请检查ZLM的配置是否与WVP的一致"); return; + }else { + logger.info("[ZLM] 正在连接 : {} -> {}:{}", + zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); } serverItem.setHookAliveInterval(zlmServerConfig.getHookAliveInterval()); if (serverItem.getHttpPort() == 0) { @@ -356,55 +386,83 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR } serverItem.setStatus(true); - if (StringUtils.isEmpty(serverItem.getId())) { - serverItem.setId(zlmServerConfig.getGeneralMediaServerId()); - mediaServerMapper.updateByHostAndPort(serverItem); - }else { - mediaServerMapper.update(serverItem); + if (ObjectUtils.isEmpty(serverItem.getId())) { + logger.warn("[未注册的zlm] serverItem缺少ID, 无法接入:{}:{}", zlmServerConfig.getIp(),zlmServerConfig.getHttpPort() ); + return; } - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId(); - if (redisUtil.get(key) == null) { + mediaServerMapper.update(serverItem); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + zlmServerConfig.getGeneralMediaServerId(); + if (RedisUtil.get(key) == null) { SsrcConfig ssrcConfig = new SsrcConfig(zlmServerConfig.getGeneralMediaServerId(), null, sipConfig.getDomain()); serverItem.setSsrcConfig(ssrcConfig); }else { - MediaServerItem mediaServerItemInRedis = (MediaServerItem)redisUtil.get(key); + MediaServerItem mediaServerItemInRedis = (MediaServerItem)RedisUtil.get(key); serverItem.setSsrcConfig(mediaServerItemInRedis.getSsrcConfig()); } - redisUtil.set(key, serverItem); + RedisUtil.set(key, serverItem); resetOnlineServerItem(serverItem); - updateMediaServerKeepalive(serverItem.getId(), null); - setZLMConfig(serverItem); + if (serverItem.isAutoConfig()) { + setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable())); + } + final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId(); + dynamicTask.stop(zlmKeepaliveKey); + dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (Math.getExponent(serverItem.getHookAliveInterval()) + 5) * 1000); publisher.zlmOnlineEventPublish(serverItem.getId()); + logger.info("[ZLM] 连接成功 {} - {}:{} ", + zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort()); + } + class KeepAliveTimeoutRunnable implements Runnable{ + + private MediaServerItem serverItem; + + public KeepAliveTimeoutRunnable(MediaServerItem serverItem) { + this.serverItem = serverItem; + } + + @Override + public void run() { + logger.info("[zlm心跳到期]:" + serverItem.getId()); + // 发起http请求验证zlm是否确实无法连接,如果确实无法连接则发送离线事件,否则不作处理 + JSONObject mediaServerConfig = zlmresTfulUtils.getMediaServerConfig(serverItem); + if (mediaServerConfig != null && mediaServerConfig.getInteger("code") == 0) { + logger.info("[zlm心跳到期]:{}验证后zlm仍在线,恢复心跳信息,请检查zlm是否可以正常向wvp发送心跳", serverItem.getId()); + // 添加zlm信息 + updateMediaServerKeepalive(serverItem.getId(), null); + }else { + publisher.zlmOfflineEventPublish(serverItem.getId()); + } + } } @Override public void zlmServerOffline(String mediaServerId) { delete(mediaServerId); + final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerId; + dynamicTask.stop(zlmKeepaliveKey); } @Override public void resetOnlineServerItem(MediaServerItem serverItem) { // 更新缓存 - String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetup.getServerId(); + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); // 使用zset的分数作为当前并发量, 默认值设置为0 - if (redisUtil.zScore(key, serverItem.getId()) == null) { // 不存在则设置默认值 已存在则重置 - redisUtil.zAdd(key, serverItem.getId(), 0L); + if (RedisUtil.zScore(key, serverItem.getId()) == null) { // 不存在则设置默认值 已存在则重置 + RedisUtil.zAdd(key, serverItem.getId(), 0L); // 查询服务流数量 - zlmresTfulUtils.getMediaList(serverItem, null, null, "rtmp",(mediaList ->{ + zlmresTfulUtils.getMediaList(serverItem, null, null, "rtsp",(mediaList ->{ Integer code = mediaList.getInteger("code"); if (code == 0) { JSONArray data = mediaList.getJSONArray("data"); if (data != null) { - redisUtil.zAdd(key, serverItem.getId(), data.size()); + RedisUtil.zAdd(key, serverItem.getId(), data.size()); } } })); }else { clearRTPServer(serverItem); } - } @@ -413,15 +471,15 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR if (mediaServerId == null) { return; } - String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetup.getServerId(); - redisUtil.zIncrScore(key, mediaServerId, 1); + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + RedisUtil.zIncrScore(key, mediaServerId, 1); } @Override public void removeCount(String mediaServerId) { - String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetup.getServerId(); - redisUtil.zIncrScore(key, mediaServerId, - 1); + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); + RedisUtil.zIncrScore(key, mediaServerId, - 1); } /** @@ -429,76 +487,120 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR * @return MediaServerItem */ @Override - public MediaServerItem getMediaServerForMinimumLoad() { - String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetup.getServerId(); + public MediaServerItem getMediaServerForMinimumLoad(Boolean hasAssist) { + String key = VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(); - if (redisUtil.zSize(key) == null || redisUtil.zSize(key) == 0) { - logger.info("获取负载最低的节点时无在线节点"); - return null; + if (RedisUtil.zSize(key) == null || RedisUtil.zSize(key) == 0) { + if (RedisUtil.zSize(key) == null || RedisUtil.zSize(key) == 0) { + logger.info("获取负载最低的节点时无在线节点"); + return null; + } } // 获取分数最低的,及并发最低的 - Set objects = redisUtil.ZRange(key, 0, -1); + Set objects = RedisUtil.zRange(key, 0, -1); ArrayList mediaServerObjectS = new ArrayList<>(objects); + MediaServerItem mediaServerItem = null; + if (hasAssist == null) { + String mediaServerId = (String)mediaServerObjectS.get(0); + mediaServerItem = getOne(mediaServerId); + }else if (hasAssist) { + for (Object mediaServerObject : mediaServerObjectS) { + String mediaServerId = (String)mediaServerObject; + MediaServerItem serverItem = getOne(mediaServerId); + if (serverItem.getRecordAssistPort() > 0) { + mediaServerItem = serverItem; + break; + } + } + }else if (!hasAssist) { + for (Object mediaServerObject : mediaServerObjectS) { + String mediaServerId = (String)mediaServerObject; + MediaServerItem serverItem = getOne(mediaServerId); + if (serverItem.getRecordAssistPort() == 0) { + mediaServerItem = serverItem; + break; + } + } + } - String mediaServerId = (String)mediaServerObjectS.get(0); - return getOne(mediaServerId); + return mediaServerItem; } /** * 对zlm服务器进行基础配置 * @param mediaServerItem 服务ID + * @param restart 是否重启zlm */ @Override - public void setZLMConfig(MediaServerItem mediaServerItem) { - logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm", + public void setZLMConfig(MediaServerItem mediaServerItem, boolean restart) { + logger.info("[ZLM] 正在设置 :{} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); String protocol = sslEnabled ? "https" : "http"; String hookPrex = String.format("%s://%s:%s/index/hook", protocol, mediaServerItem.getHookIp(), serverPort); - String recordHookPrex = null; - if (mediaServerItem.getRecordAssistPort() != 0) { - recordHookPrex = String.format("http://127.0.0.1:%s/api/record", mediaServerItem.getRecordAssistPort()); - } + Map param = new HashMap<>(); param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline - param.put("ffmpeg.cmd","%s -fflags nobuffer -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"); param.put("hook.enable","1"); param.put("hook.on_flow_report",""); param.put("hook.on_play",String.format("%s/on_play", hookPrex)); param.put("hook.on_http_access",""); param.put("hook.on_publish", String.format("%s/on_publish", hookPrex)); - param.put("hook.on_record_mp4",recordHookPrex != null? String.format("%s/on_record_mp4", recordHookPrex): ""); param.put("hook.on_record_ts",""); param.put("hook.on_rtsp_auth",""); param.put("hook.on_rtsp_realm",""); param.put("hook.on_server_started",String.format("%s/on_server_started", hookPrex)); - param.put("hook.on_shell_login",String.format("%s/on_shell_login", hookPrex)); + param.put("hook.on_shell_login",""); param.put("hook.on_stream_changed",String.format("%s/on_stream_changed", hookPrex)); param.put("hook.on_stream_none_reader",String.format("%s/on_stream_none_reader", hookPrex)); param.put("hook.on_stream_not_found",String.format("%s/on_stream_not_found", hookPrex)); param.put("hook.on_server_keepalive",String.format("%s/on_server_keepalive", hookPrex)); + param.put("hook.on_send_rtp_stopped",String.format("%s/on_send_rtp_stopped", hookPrex)); + param.put("hook.on_rtp_server_timeout",String.format("%s/on_rtp_server_timeout", hookPrex)); + if (mediaServerItem.getRecordAssistPort() > 0) { + param.put("hook.on_record_mp4",String.format("http://127.0.0.1:%s/api/record/on_record_mp4", mediaServerItem.getRecordAssistPort())); + }else { + param.put("hook.on_record_mp4",""); + } param.put("hook.timeoutSec","20"); - param.put("general.streamNoneReaderDelayMS","-1".equals(mediaServerItem.getStreamNoneReaderDelayMS())?"3600000":mediaServerItem.getStreamNoneReaderDelayMS() ); + // 推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。 + // 置0关闭此特性(推流断开会导致立即断开播放器) + // 此参数不应大于播放器超时时间 + // 优化此消息以更快的收到流注销事件 + param.put("protocol.continue_push_ms", "3000" ); + // 最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track, 设置此选项优化那些音频错误的不规范流, + // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项 +// param.put("general.wait_track_ready_ms", "3000" ); + if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) { + param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-")); + } JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param); if (responseJSON != null && responseJSON.getInteger("code") == 0) { - logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm成功", - mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + if (restart) { + logger.info("[ZLM] 设置成功,开始重启以保证配置生效 {} -> {}:{}", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + zlmresTfulUtils.restartServer(mediaServerItem); + }else { + logger.info("[ZLM] 设置成功 {} -> {}:{}", + mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); + } + + }else { - logger.info("[ ZLM:{} ]-[ {}:{} ]设置zlm失败", + logger.info("[ZLM] 设置zlm失败 {} -> {}:{}", mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort()); } + + } @Override - public WVPResult checkMediaServer(String ip, int port, String secret) { - WVPResult result = new WVPResult<>(); + public MediaServerItem checkMediaServer(String ip, int port, String secret) { if (mediaServerMapper.queryOneByHostAndPort(ip, port) != null) { - result.setCode(-1); - result.setMsg("此连接已存在"); - return result; + throw new ControllerException(ErrorCode.ERROR100.getCode(), "此连接已存在"); } MediaServerItem mediaServerItem = new MediaServerItem(); mediaServerItem.setIp(ip); @@ -506,21 +608,15 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR mediaServerItem.setSecret(secret); JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); if (responseJSON == null) { - result.setCode(-1); - result.setMsg("连接失败"); - return result; + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接失败"); } JSONArray data = responseJSON.getJSONArray("data"); ZLMServerConfig zlmServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class); if (zlmServerConfig == null) { - result.setCode(-1); - result.setMsg("读取配置失败"); - return result; + throw new ControllerException(ErrorCode.ERROR100.getCode(), "读取配置失败"); } if (mediaServerMapper.queryOne(zlmServerConfig.getGeneralMediaServerId()) != null) { - result.setCode(-1); - result.setMsg("媒体服务ID [" + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置"); - return result; + throw new ControllerException(ErrorCode.ERROR100.getCode(), "媒体服务ID [" + zlmServerConfig.getGeneralMediaServerId() + " ] 已存在,请修改媒体服务器配置"); } mediaServerItem.setHttpSSlPort(zlmServerConfig.getHttpPort()); mediaServerItem.setRtmpPort(zlmServerConfig.getRtmpPort()); @@ -529,13 +625,9 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR mediaServerItem.setRtspSSLPort(zlmServerConfig.getRtspSSlport()); mediaServerItem.setRtpProxyPort(zlmServerConfig.getRtpProxyPort()); mediaServerItem.setStreamIp(ip); - mediaServerItem.setHookIp(sipConfig.getIp()); + mediaServerItem.setHookIp(sipConfig.getIp().split(",")[0]); mediaServerItem.setSdpIp(ip); - mediaServerItem.setStreamNoneReaderDelayMS(zlmServerConfig.getGeneralStreamNoneReaderDelayMS()); - result.setCode(0); - result.setMsg("成功"); - result.setData(mediaServerItem); - return result; + return mediaServerItem; } @Override @@ -543,9 +635,6 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR boolean result = false; OkHttpClient client = new OkHttpClient(); String url = String.format("http://%s:%s/index/api/record", ip, port); - - FormBody.Builder builder = new FormBody.Builder(); - Request request = new Request.Builder() .get() .url(url) @@ -562,20 +651,76 @@ public class MediaServerServiceImpl implements IMediaServerService, CommandLineR @Override public void delete(String id) { - redisUtil.zRemove(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetup.getServerId(), id); - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetup.getServerId() + "_" + id; - redisUtil.del(key); + RedisUtil.zRemove(VideoManagerConstants.MEDIA_SERVERS_ONLINE_PREFIX + userSetting.getServerId(), id); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + id; + RedisUtil.del(key); + } + @Override + public void deleteDb(String id){ + //同步删除数据库中的数据 + mediaServerMapper.delOne(id); } @Override - public void updateMediaServerKeepalive(String mediaServerId, JSONObject data) { + public void updateMediaServerKeepalive(String mediaServerId, ServerKeepaliveData data) { MediaServerItem mediaServerItem = getOne(mediaServerId); if (mediaServerItem == null) { - logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息"); - return; + // 缓存不存在,从数据库查询,如果数据库不存在则是错误的 + mediaServerItem = getOneFromDatabase(mediaServerId); + if (mediaServerItem == null) { + logger.warn("[更新ZLM 保活信息]失败,未找到流媒体信息"); + return; + } + // zlm连接重试 + logger.warn("[更新ZLM 保活信息]尝试链接zml id {}", mediaServerId); + SsrcConfig ssrcConfig = new SsrcConfig(mediaServerItem.getId(), null, sipConfig.getDomain()); + mediaServerItem.setSsrcConfig(ssrcConfig); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + "_" + mediaServerItem.getId(); + RedisUtil.set(key, mediaServerItem); + clearRTPServer(mediaServerItem); } - String key = VideoManagerConstants.MEDIA_SERVER_KEEPALIVE_PREFIX + userSetup.getServerId() + "_" + mediaServerId; - int hookAliveInterval = mediaServerItem.getHookAliveInterval() + 2; - redisUtil.set(key, data, hookAliveInterval); + final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + mediaServerItem.getId(); + dynamicTask.stop(zlmKeepaliveKey); + dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(mediaServerItem), (mediaServerItem.getHookAliveInterval().intValue() + 5) * 1000); + } + + private MediaServerItem getOneFromDatabase(String mediaServerId) { + return mediaServerMapper.queryOne(mediaServerId); + } + + @Override + public void syncCatchFromDatabase() { + List allInCatch = getAll(); + List allInDatabase = mediaServerMapper.queryAll(); + Map mediaServerItemMap = new HashMap<>(); + + for (MediaServerItem mediaServerItem : allInDatabase) { + mediaServerItemMap.put(mediaServerItem.getId(), mediaServerItem); + } + for (MediaServerItem mediaServerItem : allInCatch) { + if (!mediaServerItemMap.containsKey(mediaServerItem.getId())) { + delete(mediaServerItem.getId()); + } + } + } + + @Override + public boolean checkRtpServer(MediaServerItem mediaServerItem, String app, String stream) { + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaServerItem, stream); + if(rtpInfo.getInteger("code") == 0){ + return rtpInfo.getBoolean("exist"); + } + return false; + } + + @Override + public MediaServerLoad getLoad(MediaServerItem mediaServerItem) { + MediaServerLoad result = new MediaServerLoad(); + result.setId(mediaServerItem.getId()); + result.setPush(redisCatchStorage.getPushStreamCount(mediaServerItem.getId())); + result.setProxy(redisCatchStorage.getProxyStreamCount(mediaServerItem.getId())); + result.setGbReceive(redisCatchStorage.getGbReceiveCount(mediaServerItem.getId())); + result.setGbSend(redisCatchStorage.getGbSendCount(mediaServerItem.getId())); + return result; } } 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 9e5221bc..b8241d7a 100644 --- 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,18 +1,23 @@ package com.genersoft.iot.vmp.service.impl; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.common.StreamURL; +import com.genersoft.iot.vmp.conf.MediaConfig; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; -import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; import com.genersoft.iot.vmp.service.IMediaServerService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.service.IMediaService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.net.URL; @Service public class MediaServiceImpl implements IMediaService { @@ -21,42 +26,54 @@ public class MediaServiceImpl implements IMediaService { private IRedisCatchStorage redisCatchStorage; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private IMediaServerService mediaServerService; + + @Autowired + private MediaConfig mediaConfig; + @Autowired private ZLMRESTfulUtils zlmresTfulUtils; @Override - public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks) { - return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null); + public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String callId) { + return getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, null, callId); } @Override - public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr) { + public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, String addr, boolean authority) { StreamInfo streamInfo = null; - - MediaServerItem mediaInfo; if (mediaServerId == null) { - mediaInfo = mediaServerService.getDefaultMediaServer(); - }else { - mediaInfo = mediaServerService.getOne(mediaServerId); + mediaServerId = mediaConfig.getId(); } + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); if (mediaInfo == null) { - return streamInfo; + return null; + } + String calld = null; + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); + if (streamAuthorityInfo != null) { + calld = streamAuthorityInfo.getCallId(); } JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, app, stream); if (mediaList != null) { if (mediaList.getInteger("code") == 0) { JSONArray data = mediaList.getJSONArray("data"); - if (data == null) return null; + if (data == null) { + return null; + } JSONObject mediaJSON = JSON.parseObject(JSON.toJSONString(data.get(0)), JSONObject.class); JSONArray tracks = mediaJSON.getJSONArray("tracks"); - streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks); + if (authority) { + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr, calld); + }else { + streamInfo = getStreamInfoByAppAndStream(mediaInfo, app, stream, tracks, addr,null); + } } } return streamInfo; @@ -65,47 +82,29 @@ public class MediaServiceImpl implements IMediaService { @Override - public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId) { - return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null); + public StreamInfo getStreamInfoByAppAndStreamWithCheck(String app, String stream, String mediaServerId, boolean authority) { + return getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, null, authority); } @Override - public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr) { + public StreamInfo getStreamInfoByAppAndStream(MediaServerItem mediaInfo, String app, String stream, Object tracks, String addr, String callId) { StreamInfo streamInfoResult = new StreamInfo(); - streamInfoResult.setStreamId(stream); + streamInfoResult.setStream(stream); streamInfoResult.setApp(app); if (addr == null) { addr = mediaInfo.getStreamIp(); } + + streamInfoResult.setIp(addr); streamInfoResult.setMediaServerId(mediaInfo.getId()); - streamInfoResult.setRtmp(String.format("rtmp://%s:%s/%s/%s", addr, mediaInfo.getRtmpPort(), app, stream)); - if (mediaInfo.getRtmpSSlPort() != 0) { - streamInfoResult.setRtmps(String.format("rtmps://%s:%s/%s/%s", addr, mediaInfo.getRtmpSSlPort(), app, stream)); - } - streamInfoResult.setRtsp(String.format("rtsp://%s:%s/%s/%s", addr, mediaInfo.getRtspPort(), app, stream)); - if (mediaInfo.getRtspSSLPort() != 0) { - streamInfoResult.setRtsps(String.format("rtsps://%s:%s/%s/%s", addr, mediaInfo.getRtspSSLPort(), app, stream)); - } - streamInfoResult.setFlv(String.format("http://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpPort(), app, stream)); - streamInfoResult.setWs_flv(String.format("ws://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpPort(), app, stream)); - streamInfoResult.setHls(String.format("http://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpPort(), app, stream)); - streamInfoResult.setWs_hls(String.format("ws://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpPort(), app, stream)); - streamInfoResult.setFmp4(String.format("http://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpPort(), app, stream)); - streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpPort(), app, stream)); - streamInfoResult.setTs(String.format("http://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpPort(), app, stream)); - streamInfoResult.setWs_ts(String.format("ws://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpPort(), app, stream)); - if (mediaInfo.getHttpSSlPort() != 0) { - streamInfoResult.setHttps_flv(String.format("https://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpSSlPort(), app, stream)); - streamInfoResult.setWss_flv(String.format("wss://%s:%s/%s/%s.flv", addr, mediaInfo.getHttpSSlPort(), app, stream)); - streamInfoResult.setHttps_hls(String.format("https://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpSSlPort(), app, stream)); - streamInfoResult.setWss_hls(String.format("wss://%s:%s/%s/%s/hls.m3u8", addr, mediaInfo.getHttpSSlPort(), app, stream)); - streamInfoResult.setHttps_fmp4(String.format("https://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpSSlPort(), app, stream)); - streamInfoResult.setWss_fmp4(String.format("wss://%s:%s/%s/%s.live.mp4", addr, mediaInfo.getHttpSSlPort(), app, stream)); - streamInfoResult.setHttps_ts(String.format("https://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpSSlPort(), app, stream)); - streamInfoResult.setWss_ts(String.format("wss://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpSSlPort(), app, stream)); - streamInfoResult.setWss_ts(String.format("wss://%s:%s/%s/%s.live.ts", addr, mediaInfo.getHttpSSlPort(), app, stream)); - streamInfoResult.setRtc(String.format("https://%s:%s/index/api/webrtc?app=%s&stream=%s&type=play", mediaInfo.getStreamIp(), mediaInfo.getHttpSSlPort(), app, stream)); - } + String callIdParam = ObjectUtils.isEmpty(callId)?"":"?callId=" + callId; + streamInfoResult.setRtmp(addr, mediaInfo.getRtmpPort(),mediaInfo.getRtmpSSlPort(), app, stream, callIdParam); + streamInfoResult.setRtsp(addr, mediaInfo.getRtspPort(),mediaInfo.getRtspSSLPort(), app, stream, callIdParam); + streamInfoResult.setFlv(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setFmp4(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setHls(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setTs(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); + streamInfoResult.setRtc(addr, mediaInfo.getHttpPort(),mediaInfo.getHttpSSlPort(), app, stream, callIdParam); streamInfoResult.setTracks(tracks); return streamInfoResult; 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 new file mode 100644 index 00000000..601ff5de --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java @@ -0,0 +1,131 @@ +package com.genersoft.iot.vmp.service.impl; + +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.gb28181.bean.TreeType; +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.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author lin + */ +@Service +public class PlatformChannelServiceImpl implements IPlatformChannelService { + + private final static Logger logger = LoggerFactory.getLogger(PlatformChannelServiceImpl.class); + + @Autowired + private PlatformChannelMapper platformChannelMapper; + + @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 result = 0; + if (channelReducesToAdd.size() > 0) { + result = platformChannelMapper.addChannels(platformId, channelReducesToAdd); + // TODO 后续给平台增加控制开关以控制是否响应目录订阅 + List deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform); + if (deviceChannelList != null) { + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD); + } + } + + return result; + } + + private List getDeviceChannelListByChannelReduceList(List channelReduces, String catalogId, ParentPlatform platform) { + List deviceChannelList = new ArrayList<>(); + if (channelReduces.size() > 0){ + PlatformCatalog catalog = catalogManager.select(catalogId); + 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); + deviceChannelList.add(deviceChannel); + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)){ + deviceChannel.setCivilCode(catalogId); + }else if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)){ + deviceChannel.setParentId(catalogId); + if (catalog != null) { + deviceChannel.setBusinessGroupId(catalog.getBusinessGroupId()); + } + } + } + } + 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 = platform.getDeviceGBId(); + } + + if ((result = platformChannelMapper.delChannelForGBByCatalogId(platformId, catalogId)) > 0) { + List deviceChannels = platformChannelMapper.queryAllChannelInCatalog(platformId, catalogId); + eventPublisher.catalogEventPublish(platformId, deviceChannels, CatalogEvent.DEL); + } + return result; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java new file mode 100644 index 00000000..136689ca --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java @@ -0,0 +1,313 @@ +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.*; +import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.IPlatformService; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +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.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.stereotype.Service; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author lin + */ +@Service +public class PlatformServiceImpl implements IPlatformService { + + private final static String REGISTER_KEY_PREFIX = "platform_register_"; + private final static String KEEPALIVE_KEY_PREFIX = "platform_keepalive_"; + + private final static Logger logger = LoggerFactory.getLogger(PlatformServiceImpl.class); + + @Autowired + private ParentPlatformMapper platformMapper; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private SIPCommanderFroPlatform commanderForPlatform; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private SubscribeHolder subscribeHolder; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private UserSetting userSetting; + + + + @Override + public ParentPlatform queryPlatformByServerGBId(String platformGbId) { + return platformMapper.getParentPlatByServerGBId(platformGbId); + } + + @Override + public PageInfo queryParentPlatformList(int page, int count) { + PageHelper.startPage(page, count); + List all = platformMapper.getParentPlatformList(); + return new PageInfo<>(all); + } + + @Override + public boolean add(ParentPlatform parentPlatform) { + + if (parentPlatform.getCatalogGroup() == 0) { + // 每次发送目录的数量默认为1 + parentPlatform.setCatalogGroup(1); + } + if (parentPlatform.getAdministrativeDivision() == null) { + // 行政区划默认去编号的前6位 + parentPlatform.setAdministrativeDivision(parentPlatform.getServerGBId().substring(0,6)); + } + parentPlatform.setCatalogId(parentPlatform.getDeviceGBId()); + int result = platformMapper.addParentPlatform(parentPlatform); + // 添加缓存 + ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + parentPlatformCatch.setParentPlatform(parentPlatform); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + if (parentPlatform.isEnable()) { + // 保存时启用就发送注册 + // 注册成功时由程序直接调用了online方法 + try { + commanderForPlatform.register(parentPlatform, eventResult -> { + logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId()); + }, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联: {}", e.getMessage()); + } + } + return result > 0; + } + + @Override + public void online(ParentPlatform parentPlatform) { + logger.info("[国标级联]:{}, 平台上线/更新注册", parentPlatform.getServerGBId()); + platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), true); + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + if (parentPlatformCatch != null) { + parentPlatformCatch.getParentPlatform().setStatus(true); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + }else { + parentPlatformCatch = new ParentPlatformCatch(); + parentPlatformCatch.setParentPlatform(parentPlatform); + parentPlatformCatch.setId(parentPlatform.getServerGBId()); + parentPlatform.setStatus(true); + parentPlatformCatch.setParentPlatform(parentPlatform); + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); + } + + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + if (!dynamicTask.isAlive(registerTaskKey)) { + // 添加注册任务 + dynamicTask.startCron(registerTaskKey, + // 注册失败(注册成功时由程序直接调用了online方法) + ()-> { + registerTask(parentPlatform); + }, + (parentPlatform.getExpires() - 10) *1000); + } + + + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId(); + if (!dynamicTask.contains(keepaliveTaskKey)) { + // 添加心跳任务 + dynamicTask.startCron(keepaliveTaskKey, + ()-> { + try { + commanderForPlatform.keepalive(parentPlatform, eventResult -> { + // 心跳失败 + if (eventResult.type == SipSubscribe.EventResultType.timeout) { + // 心跳超时 + ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + // 此时是第三次心跳超时, 平台离线 + if (platformCatch.getKeepAliveReply() == 2) { + // 设置平台离线,并重新注册 + logger.info("[国标级联] {},三次心跳超时后再次发起注册", parentPlatform.getServerGBId()); + try { + commanderForPlatform.register(parentPlatform, eventResult1 -> { + logger.info("[国标级联] {},三次心跳超时后再次发起注册仍然失败,开始定时发起注册,间隔为1分钟", parentPlatform.getServerGBId()); + offline(parentPlatform, false); + }, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage()); + } + } + + }else { + logger.warn("[国标级联]发送心跳收到错误,code: {}, msg: {}", eventResult.statusCode, eventResult.msg); + } + + }, eventResult -> { + // 心跳成功 + // 清空之前的心跳超时计数 + ParentPlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); + if (platformCatch.getKeepAliveReply() > 0) { + platformCatch.setKeepAliveReply(0); + redisCatchStorage.updatePlatformCatchInfo(platformCatch); + } + }); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage()); + } + }, + (parentPlatform.getKeepTimeout() - 10)*1000); + } + } + + private void registerTask(ParentPlatform parentPlatform){ + try { + // 设置超时重发, 后续从底层支持消息重发 + String key = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId() + "_timeout"; + if (dynamicTask.isAlive(key)) { + return; + } + dynamicTask.startDelay(key, ()->{ + registerTask(parentPlatform); + }, 1000); + logger.info("[国标级联] 平台:{}注册即将到期,重新注册", parentPlatform.getServerGBId()); + commanderForPlatform.register(parentPlatform, eventResult -> { + dynamicTask.stop(key); + offline(parentPlatform, false); + },eventResult -> { + dynamicTask.stop(key); + }); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.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); + + // 停止所有推流 + logger.info("[平台离线] {}, 停止所有推流", parentPlatform.getServerGBId()); + stopAllPush(parentPlatform.getServerGBId()); + if (stopRegister) { + // 清除注册定时 + logger.info("[平台离线] {}, 停止定时注册任务", parentPlatform.getServerGBId()); + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + if (dynamicTask.contains(registerTaskKey)) { + dynamicTask.stop(registerTaskKey); + } + } + // 清除心跳定时 + logger.info("[平台离线] {}, 停止定时发送心跳任务", parentPlatform.getServerGBId()); + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId(); + if (dynamicTask.contains(keepaliveTaskKey)) { + // 添加心跳任务 + dynamicTask.stop(keepaliveTaskKey); + } + // 停止目录订阅回复 + logger.info("[平台离线] {}, 停止订阅回复", parentPlatform.getServerGBId()); + subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId()); + } + + private void stopAllPush(String platformId) { + List sendRtpItems = redisCatchStorage.querySendRTPServer(platformId); + if (sendRtpItems != null && sendRtpItems.size() > 0) { + for (SendRtpItem sendRtpItem : sendRtpItems) { + redisCatchStorage.deleteSendRTPServer(platformId, sendRtpItem.getChannelId(), null, null); + MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId()); + Map param = new HashMap<>(3); + param.put("vhost", "__defaultVhost__"); + param.put("app", sendRtpItem.getApp()); + param.put("stream", sendRtpItem.getStreamId()); + zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param); + } + } + } + + @Override + public void login(ParentPlatform parentPlatform) { + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId(); + try { + commanderForPlatform.register(parentPlatform, eventResult1 -> { + logger.info("[国标级联] {},开始定时发起注册,间隔为1分钟", parentPlatform.getServerGBId()); + // 添加注册任务 + dynamicTask.startCron(registerTaskKey, + // 注册失败(注册成功时由程序直接调用了online方法) + ()->logger.info("[国标级联] {},平台离线后持续发起注册,失败", parentPlatform.getServerGBId()), + 60*1000); + }, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联注册: {}", e.getMessage()); + } + } + + @Override + public void sendNotifyMobilePosition(String platformId) { + ParentPlatform 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) { + return; + } + for (DeviceChannel deviceChannel : gbStreams) { + String gbId = deviceChannel.getChannelId(); + GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(gbId); + // 无最新位置不发送 + if (gpsMsgInfo != null) { + // 经纬度都为0不发送 + if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) { + continue; + } + // 发送GPS消息 + try { + commanderForPlatform.sendNotifyMobilePosition(platform, gpsMsgInfo, subscribe); + } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | + IllegalAccessException e) { + logger.error("[命令发送失败] 国标级联 移动位置通知: {}", e.getMessage()); + } + } + } + } + } +} 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 index dab637f7..b8e7c8e5 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java @@ -1,41 +1,54 @@ package com.genersoft.iot.vmp.service.impl; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +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.VideoStreamSessionManager; 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.zlm.ZLMHttpHookSubscribe; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform; +import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.IDeviceService; import com.genersoft.iot.vmp.service.IMediaServerService; -import com.genersoft.iot.vmp.service.bean.SSRCInfo; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; import com.genersoft.iot.vmp.service.IMediaService; import com.genersoft.iot.vmp.service.IPlayService; -import gov.nist.javax.sip.stack.SIPDialog; +import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback; +import com.genersoft.iot.vmp.service.bean.PlayBackCallback; +import com.genersoft.iot.vmp.service.bean.PlayBackResult; +import com.genersoft.iot.vmp.service.bean.SSRCInfo; +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.utils.redis.RedisUtil; +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 org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.util.ResourceUtils; -import org.springframework.web.context.request.async.DeferredResult; +import org.springframework.util.ObjectUtils; -import java.io.FileNotFoundException; -import java.util.Objects; +import javax.sip.InvalidArgumentException; +import javax.sip.ResponseEvent; +import javax.sip.SipException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.ParseException; +import java.util.List; import java.util.UUID; @SuppressWarnings(value = {"rawtypes", "unchecked"}) @@ -45,16 +58,16 @@ public class PlayServiceImpl implements IPlayService { private final static Logger logger = LoggerFactory.getLogger(PlayServiceImpl.class); @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private SIPCommander cmder; @Autowired - private IRedisCatchStorage redisCatchStorage; + private SIPCommanderFroPlatform sipCommanderFroPlatform; @Autowired - private RedisUtil redis; + private IRedisCatchStorage redisCatchStorage; @Autowired private DeferredResultHolder resultHolder; @@ -62,6 +75,9 @@ public class PlayServiceImpl implements IPlayService { @Autowired private ZLMRESTfulUtils zlmresTfulUtils; + @Autowired + private AssistRESTfulUtils assistRESTfulUtils; + @Autowired private IMediaService mediaService; @@ -71,209 +87,328 @@ public class PlayServiceImpl implements IPlayService { @Autowired private VideoStreamSessionManager streamSession; + @Autowired - private UserSetup userSetup; + private IDeviceService deviceService; + + @Autowired + private UserSetting userSetting; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ZlmHttpHookSubscribe subscribe; @Override - public PlayResult play(MediaServerItem mediaServerItem, String deviceId, String channelId, ZLMHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent) { - PlayResult playResult = new PlayResult(); - RequestMessage msg = new RequestMessage(); - String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; - msg.setKey(key); - String uuid = UUID.randomUUID().toString(); - msg.setId(uuid); - playResult.setUuid(uuid); - DeferredResult> result = new DeferredResult<>(userSetup.getPlayTimeout()); - playResult.setResult(result); - // 录像查询以channelId作为deviceId查询 - resultHolder.put(key, uuid, result); + public void play(MediaServerItem mediaServerItem, String deviceId, String channelId, + ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, + Runnable timeoutCallback) { if (mediaServerItem == null) { - WVPResult wvpResult = new WVPResult(); - wvpResult.setCode(-1); - wvpResult.setMsg("未找到可用的zlm"); - msg.setData(wvpResult); - resultHolder.invokeResult(msg); - return playResult; + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm"); } + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; + + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + Device device = redisCatchStorage.getDevice(deviceId); StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); - playResult.setDevice(device); - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备点播超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - WVPResult wvpResult = new WVPResult(); - wvpResult.setCode(-1); - SIPDialog dialog = streamSession.getDialog(deviceId, channelId); - if (dialog != null) { - wvpResult.setMsg("收流超时,请稍候重试"); - }else { - wvpResult.setMsg("点播超时,请稍候重试"); + + if (streamInfo != null) { + String streamId = streamInfo.getStream(); + if (streamId == null) { + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("点播失败, redis缓存streamId等于null"); + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + return; } - msg.setData(wvpResult); - // 点播超时回复BYE - cmder.streamByeCmd(device.getDeviceId(), channelId); - // 释放rtpserver - mediaServerService.closeRTPServer(playResult.getDevice(), channelId); - // 回复之前所有的点播请求 - resultHolder.invokeAllResult(msg); - }); - result.onCompletion(()->{ - // 点播结束时调用截图接口 - try { - String classPath = ResourceUtils.getURL("classpath:").getPath(); - // System.out.println(classPath); - // 兼容打包为jar的class路径 - if(classPath.contains("jar")) { - classPath = classPath.substring(0, classPath.lastIndexOf(".")); - classPath = classPath.substring(0, classPath.lastIndexOf("/") + 1); - } - if (classPath.startsWith("file:")) { - classPath = classPath.substring(classPath.indexOf(":") + 1); - } - String path = classPath + "static/static/snap/"; - // 兼容Windows系统路径(去除前面的“/”) - if(System.getProperty("os.name").contains("indows")) { - path = path.substring(1); - } - String fileName = deviceId + "_" + channelId + ".jpg"; - ResponseEntity responseEntity = (ResponseEntity)result.getResult(); - if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) { - WVPResult wvpResult = (WVPResult)responseEntity.getBody(); - if (Objects.requireNonNull(wvpResult).getCode() == 0) { - StreamInfo streamInfoForSuccess = (StreamInfo)wvpResult.getData(); - MediaServerItem mediaInfo = mediaServerService.getOne(streamInfoForSuccess.getMediaServerId()); - String streamUrl = streamInfoForSuccess.getFmp4(); - // 请求截图 - zlmresTfulUtils.getSnap(mediaInfo, streamUrl, 15, 1, path, fileName); + String mediaServerId = streamInfo.getMediaServerId(); + MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); + + JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); + if (rtpInfo.getInteger("code") == 0) { + if (rtpInfo.getBoolean("exist")) { + int localPort = rtpInfo.getInteger("local_port"); + if (localPort == 0) { + logger.warn("[点播],点播时发现rtpServerC存在,但是尚未开始推流"); + // 此时说明rtpServer已经创建但是流还没有推上来 + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("点播已经在进行中,请稍候重试"); + msg.setData(wvpResult); + + resultHolder.invokeAllResult(msg); + return; + } else { + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); + wvpResult.setData(streamInfo); + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + if (hookEvent != null) { + hookEvent.response(mediaServerItem, JSON.parseObject(JSON.toJSONString(streamInfo))); + } } + + } else { + redisCatchStorage.stopPlay(streamInfo); + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + streamInfo = null; } - } catch (FileNotFoundException e) { - e.printStackTrace(); + } else { + //zlm连接失败 + redisCatchStorage.stopPlay(streamInfo); + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + streamInfo = null; + } - }); + } if (streamInfo == null) { - SSRCInfo ssrcInfo; String streamId = null; if (mediaServerItem.isRtpEnable()) { streamId = String.format("%s_%s", device.getDeviceId(), channelId); } + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId, device.isSsrcCheck(), false); + if (ssrcInfo == null) { + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("开启收流失败"); + msg.setData(wvpResult); - ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId); - - // 发送点播消息 - cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - onPublishHandlerForPlay(mediaServerItemInUse, response, deviceId, channelId, uuid); + resultHolder.invokeAllResult(msg); + return; + } + play(mediaServerItem, ssrcInfo, device, channelId, (mediaServerItemInUse, response) -> { if (hookEvent != null) { hookEvent.response(mediaServerItem, response); } - }, (event) -> { + }, event -> { + // sip error错误 WVPResult wvpResult = new WVPResult(); - wvpResult.setCode(-1); - // 点播返回sip错误 - mediaServerService.closeRTPServer(playResult.getDevice(), channelId); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); wvpResult.setMsg(String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg)); msg.setData(wvpResult); resultHolder.invokeAllResult(msg); if (errorEvent != null) { errorEvent.response(event); } - - + }, (code, msgStr) -> { + // invite点播超时 + WVPResult wvpResult = new WVPResult(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + if (code == 0) { + wvpResult.setMsg("点播超时,请稍候重试"); + } else if (code == 1) { + wvpResult.setMsg("收流超时,请稍候重试"); + } + msg.setData(wvpResult); + // 回复之前所有的点播请求 + resultHolder.invokeAllResult(msg); }); - } else { - String streamId = streamInfo.getStreamId(); - if (streamId == null) { - WVPResult wvpResult = new WVPResult(); - wvpResult.setCode(-1); - wvpResult.setMsg("点播失败, redis缓存streamId等于null"); - msg.setData(wvpResult); - resultHolder.invokeAllResult(msg); - return playResult; - } - String mediaServerId = streamInfo.getMediaServerId(); - MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); - - JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); - if (rtpInfo != null && rtpInfo.getBoolean("exist")) { - - WVPResult wvpResult = new WVPResult(); - wvpResult.setCode(0); - wvpResult.setMsg("success"); - wvpResult.setData(streamInfo); - msg.setData(wvpResult); - - resultHolder.invokeAllResult(msg); - if (hookEvent != null) { - hookEvent.response(mediaServerItem, JSONObject.parseObject(JSON.toJSONString(streamInfo))); - } - } else { - // TODO 点播前是否重置状态 - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - SSRCInfo ssrcInfo; - String streamId2 = null; - if (mediaServerItem.isRtpEnable()) { - streamId2 = String.format("%s_%s", device.getDeviceId(), channelId); - } - ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, streamId2); - - cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid); - }, (event) -> { - mediaServerService.closeRTPServer(playResult.getDevice(), channelId); - WVPResult wvpResult = new WVPResult(); - wvpResult.setCode(-1); - wvpResult.setMsg(String.format("点播失败, 错误码: %s, %s", event.statusCode, event.msg)); - msg.setData(wvpResult); - resultHolder.invokeAllResult(msg); - }); - } } + } - return playResult; + + @Override + public void play(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, + ZlmHttpHookSubscribe.Event hookEvent, SipSubscribe.Event errorEvent, + InviteTimeOutCallback timeoutCallback) { + + logger.info("[点播开始] deviceId: {}, channelId: {},收流端口: {}, 收流模式:{}, SSRC: {}, SSRC校验:{}", device.getDeviceId(), channelId, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); + // 超时处理 + String timeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(timeOutTaskKey, () -> { + // 执行超时任务时查询是否已经成功,成功了则不执行超时任务,防止超时任务取消失败的情况 + if (redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId) == null) { + logger.info("[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", device.getDeviceId(), channelId, ssrcInfo.getPort(), ssrcInfo.getSsrc()); + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 + try { + cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + logger.error("[点播超时], 发送BYE失败 {}", e.getMessage()); + } finally { + timeoutCallback.run(1, "收流超时"); + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 取消订阅消息监听 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + } + } + }, userSetting.getPlayTimeout()); + //端口获取失败的ssrcInfo 没有必要发送点播指令 + if (ssrcInfo.getPort() <= 0) { + logger.info("[点播端口分配异常],deviceId={},channelId={},ssrcInfo={}", device.getDeviceId(), channelId, ssrcInfo); + dynamicTask.stop(timeOutTaskKey); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + + RequestMessage msg = new RequestMessage(); + msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAY + device.getDeviceId() + channelId); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "点播端口分配异常")); + resultHolder.invokeAllResult(msg); + return; + } + try { + cmder.playStreamCmd(mediaServerItem, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> { + logger.info("收到订阅消息: " + response.toJSONString()); + dynamicTask.stop(timeOutTaskKey); + + // hook响应 + onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId); + hookEvent.response(mediaServerItemInuse, response); + logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId); + String streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.flv", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream()); + String path = "snap"; + String fileName = device.getDeviceId() + "_" + channelId + ".jpg"; + // 请求截图 + logger.info("[请求截图]: " + fileName); + zlmresTfulUtils.getSnap(mediaServerItemInuse, streamUrl, 15, 1, path, fileName); + + }, (event) -> { + ResponseEvent responseEvent = (ResponseEvent) event.event; + String contentString = new String(responseEvent.getResponse().getRawContent()); + // 获取ssrc + int ssrcIndex = contentString.indexOf("y="); + // 检查是否有y字段 + if (ssrcIndex >= 0) { + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { + return; + } + logger.info("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { + logger.info("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + + if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { + // ssrc 不可用 + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + event.msg = "下级自定义了ssrc,但是此ssrc不可用"; + event.statusCode = 400; + errorEvent.response(event); + return; + } + + // 单端口模式streamId也有变化,需要重新设置监听 + if (!mediaServerItem.isRtpEnable()) { + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); + dynamicTask.stop(timeOutTaskKey); + // hook响应 + onPublishHandlerForPlay(mediaServerItemInUse, response, device.getDeviceId(), channelId); + hookEvent.response(mediaServerItemInUse, response); + }); + } + // 关闭rtp server + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 重新开启ssrc server + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), false, ssrcInfo.getPort()); + + } + } + }, (event) -> { + dynamicTask.stop(timeOutTaskKey); + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + errorEvent.response(event); + }); + } 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(), channelId, ssrcInfo.getStream()); + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null)); + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); + } } @Override - public void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) { + public void onPublishHandlerForPlay(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId) { + StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); RequestMessage msg = new RequestMessage(); - msg.setId(uuid); msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId); - StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid); if (streamInfo != null) { DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); if (deviceChannel != null) { - deviceChannel.setStreamId(streamInfo.getStreamId()); - storager.startPlay(deviceId, channelId, streamInfo.getStreamId()); + deviceChannel.setStreamId(streamInfo.getStream()); + storager.startPlay(deviceId, channelId, streamInfo.getStream()); } redisCatchStorage.startPlay(streamInfo); - msg.setData(JSON.toJSONString(streamInfo)); WVPResult wvpResult = new WVPResult(); - wvpResult.setCode(0); - wvpResult.setMsg("success"); + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); wvpResult.setData(streamInfo); - msg.setData(wvpResult); + msg.setData(wvpResult); resultHolder.invokeAllResult(msg); + } else { logger.warn("设备预览API调用失败!"); - msg.setData("设备预览API调用失败!"); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "设备预览API调用失败!")); resultHolder.invokeAllResult(msg); } } + private void onPublishHandlerForPlayback(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, PlayBackCallback playBackCallback) { + + StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId); + PlayBackResult playBackResult = new PlayBackResult<>(); + if (streamInfo != null) { + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(streamInfo.getStream()); + storager.startPlay(deviceId, channelId, streamInfo.getStream()); + } + redisCatchStorage.startPlay(streamInfo); + + + playBackResult.setCode(ErrorCode.SUCCESS.getCode()); + playBackResult.setMsg(ErrorCode.SUCCESS.getMsg()); + playBackResult.setData(streamInfo); + playBackCallback.call(playBackResult); + } else { + logger.warn("录像回放调用失败!"); + playBackResult.setCode(ErrorCode.ERROR100.getCode()); + playBackResult.setMsg("录像回放调用失败!"); + playBackCallback.call(playBackResult); + } + } + @Override public MediaServerItem getNewMediaServerItem(Device device) { - if (device == null) return null; - String mediaServerId = device.getMediaServerId(); + if (device == null) { + return null; + } MediaServerItem mediaServerItem; - if (mediaServerId == null) { - mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(); - }else { - mediaServerItem = mediaServerService.getOne(mediaServerId); + 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..."); @@ -281,50 +416,455 @@ public class PlayServiceImpl implements IPlayService { return mediaServerItem; } + @Override + public MediaServerItem getNewMediaServerItemHasAssist(Device device) { + if (device == null) { + return null; + } + MediaServerItem mediaServerItem; + if (ObjectUtils.isEmpty(device.getMediaServerId()) || "auto".equals(device.getMediaServerId())) { + mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(true); + } else { + mediaServerItem = mediaServerService.getOne(device.getMediaServerId()); + } + if (mediaServerItem == null) { + logger.warn("[获取可用的ZLM节点]未找到可使用的ZLM..."); + } + return mediaServerItem; + } @Override - public void onPublishHandlerForPlayBack(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) { - RequestMessage msg = new RequestMessage(); - msg.setKey(DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId); - msg.setId(uuid); - StreamInfo streamInfo = onPublishHandler(mediaServerItem, resonse, deviceId, channelId, uuid); - if (streamInfo != null) { - redisCatchStorage.startPlayback(streamInfo); - msg.setData(JSON.toJSONString(streamInfo)); - resultHolder.invokeResult(msg); - } else { - logger.warn("设备回放API调用失败!"); - msg.setData("设备回放API调用失败!"); - resultHolder.invokeResult(msg); + public void playBack(String deviceId, String channelId, String startTime, + String endTime, InviteStreamCallback inviteStreamCallback, + PlayBackCallback callback) { + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + MediaServerItem newMediaServerItem = getNewMediaServerItem(device); + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, device.isSsrcCheck(), true); + + playBack(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, inviteStreamCallback, callback); + } + + @Override + public void playBack(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, + String deviceId, String channelId, String startTime, + String endTime, InviteStreamCallback infoCallBack, + PlayBackCallback playBackCallback) { + if (mediaServerItem == null || ssrcInfo == null) { + return; + } + + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备: " + deviceId + "不存在"); + } + + PlayBackResult playBackResult = new PlayBackResult<>(); + String playBackTimeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(playBackTimeOutTaskKey, () -> { + logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); + playBackResult.setCode(ErrorCode.ERROR100.getCode()); + playBackResult.setMsg("回放超时"); + + 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()); + } + // 回复之前所有的点播请求 + playBackCallback.call(playBackResult); + }, userSetting.getPlayTimeout()); + + SipSubscribe.Event errorEvent = event -> { + dynamicTask.stop(playBackTimeOutTaskKey); + playBackResult.setCode(ErrorCode.ERROR100.getCode()); + playBackResult.setMsg(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg)); + playBackResult.setEvent(event); + playBackCallback.call(playBackResult); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + }; + + InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> { + logger.info("收到回放订阅消息: " + inviteStreamInfo.getResponse().toJSONString()); + dynamicTask.stop(playBackTimeOutTaskKey); + StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId); + if (streamInfo == null) { + logger.warn("设备回放API调用失败!"); + playBackResult.setCode(ErrorCode.ERROR100.getCode()); + playBackResult.setMsg("设备回放API调用失败!"); + playBackCallback.call(playBackResult); + return; + } + redisCatchStorage.startPlayback(streamInfo, inviteStreamInfo.getCallId()); + playBackResult.setCode(ErrorCode.SUCCESS.getCode()); + playBackResult.setMsg(ErrorCode.SUCCESS.getMsg()); + playBackResult.setData(streamInfo); + playBackResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem()); + playBackResult.setResponse(inviteStreamInfo.getResponse()); + playBackCallback.call(playBackResult); + }; + + try { + cmder.playbackStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, infoCallBack, + hookEvent, eventResult -> { + if (eventResult.type == SipSubscribe.EventResultType.response) { + ResponseEvent responseEvent = (ResponseEvent) eventResult.event; + String contentString = new String(responseEvent.getResponse().getRawContent()); + // 获取ssrc + int ssrcIndex = contentString.indexOf("y="); + // 检查是否有y字段 + if (ssrcIndex >= 0) { + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12); + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { + return; + } + logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) { + logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); + + if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) { + // ssrc 不可用 + // 释放ssrc + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc()); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用"; + eventResult.statusCode = 400; + errorEvent.response(eventResult); + return; + } + + // 单端口模式streamId也有变化,需要重新设置监听 + if (!mediaServerItem.isRtpEnable()) { + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId()); + subscribe.removeSubscribe(hookSubscribe); + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> { + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString()); + dynamicTask.stop(playBackTimeOutTaskKey); + // hook响应 + onPublishHandlerForPlayback(mediaServerItemInUse, response, device.getDeviceId(), channelId, playBackCallback); + hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream())); + }); + } + // 关闭rtp server + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream()); + // 重新开启ssrc server + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort()); + } + } + } + + }, errorEvent); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 回放: {}", e.getMessage()); + + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null)); + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); } } + @Override - public void onPublishHandlerForDownload(MediaServerItem mediaServerItem, JSONObject response, String deviceId, String channelId, String uuid) { + public void download(String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback playBackCallback) { + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + return; + } + MediaServerItem newMediaServerItem = getNewMediaServerItemHasAssist(device); + if (newMediaServerItem == null) { + PlayBackResult downloadResult = new PlayBackResult<>(); + downloadResult.setCode(ErrorCode.ERROR100.getCode()); + downloadResult.setMsg("未找到assist服务"); + playBackCallback.call(downloadResult); + return; + } + SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, device.isSsrcCheck(), true); + + download(newMediaServerItem, ssrcInfo, deviceId, channelId, startTime, endTime, downloadSpeed, infoCallBack, playBackCallback); + } + + + @Override + public void download(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, String deviceId, String channelId, String startTime, String endTime, int downloadSpeed, InviteStreamCallback infoCallBack, PlayBackCallback hookCallBack) { + if (mediaServerItem == null || ssrcInfo == null) { + return; + } + + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + "不存在"); + } + PlayBackResult downloadResult = new PlayBackResult<>(); + + String downLoadTimeOutTaskKey = UUID.randomUUID().toString(); + dynamicTask.startDelay(downLoadTimeOutTaskKey, () -> { + logger.warn(String.format("录像下载请求超时,deviceId:%s ,channelId:%s", deviceId, channelId)); + downloadResult.setCode(ErrorCode.ERROR100.getCode()); + downloadResult.setMsg("录像下载请求超时"); + hookCallBack.call(downloadResult); + + // 点播超时回复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); + downloadResult.setCode(ErrorCode.ERROR100.getCode()); + downloadResult.setMsg(String.format("录像下载失败, 错误码: %s, %s", event.statusCode, event.msg)); + downloadResult.setEvent(event); + hookCallBack.call(downloadResult); + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); + }; + + try { + cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack, + inviteStreamInfo -> { + logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString()); + dynamicTask.stop(downLoadTimeOutTaskKey); + StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId); + streamInfo.setStartTime(startTime); + streamInfo.setEndTime(endTime); + redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId()); + downloadResult.setCode(ErrorCode.SUCCESS.getCode()); + downloadResult.setMsg(ErrorCode.SUCCESS.getMsg()); + downloadResult.setData(streamInfo); + downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem()); + downloadResult.setResponse(inviteStreamInfo.getResponse()); + hookCallBack.call(downloadResult); + }, errorEvent); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 录像下载: {}", e.getMessage()); + + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(new CmdSendFailEvent(null)); + eventResult.msg = "命令发送失败"; + errorEvent.response(eventResult); + } + } + + @Override + public StreamInfo getDownLoadInfo(String deviceId, String channelId, String stream) { + StreamInfo streamInfo = redisCatchStorage.queryDownload(deviceId, channelId, stream, null); + if (streamInfo != null) { + if (streamInfo.getProgress() == 1) { + return streamInfo; + } + + // 获取当前已下载时长 + String mediaServerId = streamInfo.getMediaServerId(); + MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); + if (mediaServerItem == null) { + logger.warn("查询录像信息时发现节点已离线"); + return null; + } + if (mediaServerItem.getRecordAssistPort() > 0) { + JSONObject jsonObject = assistRESTfulUtils.fileDuration(mediaServerItem, streamInfo.getApp(), streamInfo.getStream(), null); + if (jsonObject == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接Assist服务失败"); + } + if (jsonObject.getInteger("code") == 0) { + long duration = jsonObject.getLong("data"); + + if (duration == 0) { + streamInfo.setProgress(0); + } else { + String startTime = streamInfo.getStartTime(); + String endTime = streamInfo.getEndTime(); + long start = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime); + long end = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime); + + BigDecimal currentCount = new BigDecimal(duration / 1000); + BigDecimal totalCount = new BigDecimal(end - start); + BigDecimal divide = currentCount.divide(totalCount, 2, RoundingMode.HALF_UP); + double process = divide.doubleValue(); + streamInfo.setProgress(process); + } + } + } + } + return streamInfo; + } + + @Override + public void onPublishHandlerForDownload(InviteStreamInfo inviteStreamInfo, String deviceId, String channelId, String uuid) { RequestMessage msg = new RequestMessage(); msg.setKey(DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId); msg.setId(uuid); - StreamInfo streamInfo = onPublishHandler(mediaServerItem, response, deviceId, channelId, uuid); + StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId); if (streamInfo != null) { - redisCatchStorage.startDownload(streamInfo); + redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId()); msg.setData(JSON.toJSONString(streamInfo)); resultHolder.invokeResult(msg); } else { logger.warn("设备预览API调用失败!"); - msg.setData("设备预览API调用失败!"); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "设备预览API调用失败!")); resultHolder.invokeResult(msg); } } - public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId, String uuid) { + public StreamInfo onPublishHandler(MediaServerItem mediaServerItem, JSONObject resonse, String deviceId, String channelId) { String streamId = resonse.getString("stream"); JSONArray tracks = resonse.getJSONArray("tracks"); - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem,"rtp", streamId, tracks); + StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream(mediaServerItem, "rtp", streamId, tracks, 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 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 { + String key = redisCatchStorage.queryPlaybackForKey(null, null, streamId, null); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); + if (null == streamInfo) { + logger.warn("streamId不存在!"); + throw new ServiceException("streamId不存在"); + } + streamInfo.setPause(true); + RedisUtil.set(key, streamInfo); + MediaServerItem mediaServerItem = mediaServerService.getOne(streamInfo.getMediaServerId()); + if (null == mediaServerItem) { + logger.warn("mediaServer 不存在!"); + throw new ServiceException("mediaServer不存在"); + } + // zlm 暂停RTP超时检查 + JSONObject jsonObject = zlmresTfulUtils.pauseRtpCheck(mediaServerItem, streamId); + if (jsonObject == null || jsonObject.getInteger("code") != 0) { + throw new ServiceException("暂停RTP接收失败"); + } + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); + cmder.playPauseCmd(device, streamInfo); + } + + @Override + public void resumeRtp(String streamId) throws ServiceException, InvalidArgumentException, ParseException, SipException { + String key = redisCatchStorage.queryPlaybackForKey(null, null, streamId, null); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); + if (null == streamInfo) { + logger.warn("streamId不存在!"); + throw new ServiceException("streamId不存在"); + } + streamInfo.setPause(false); + RedisUtil.set(key, streamInfo); + MediaServerItem mediaServerItem = mediaServerService.getOne(streamInfo.getMediaServerId()); + if (null == mediaServerItem) { + logger.warn("mediaServer 不存在!"); + throw new ServiceException("mediaServer不存在"); + } + // zlm 暂停RTP超时检查 + JSONObject jsonObject = zlmresTfulUtils.resumeRtpCheck(mediaServerItem, streamId); + if (jsonObject == null || jsonObject.getInteger("code") != 0) { + throw new ServiceException("继续RTP接收失败"); + } + Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); + cmder.playResumeCmd(device, streamInfo); + } } 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 index a972585a..ea0fd501 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java @@ -1,35 +1,42 @@ package com.genersoft.iot.vmp.service.impl; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +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.ZLMRESTfulUtils; -import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; -import com.genersoft.iot.vmp.media.zlm.dto.MediaItem; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; 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.IGbStreamService; import com.genersoft.iot.vmp.service.IMediaServerService; 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.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.genersoft.iot.vmp.storager.dao.GbStreamMapper; import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; import com.genersoft.iot.vmp.storager.dao.StreamProxyMapper; -import com.genersoft.iot.vmp.service.IStreamProxyService; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; 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.util.StringUtils; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * 视频代理业务 @@ -40,13 +47,13 @@ public class StreamProxyServiceImpl implements IStreamProxyService { private final static Logger logger = LoggerFactory.getLogger(StreamProxyServiceImpl.class); @Autowired - private IVideoManagerStorager videoManagerStorager; + private IVideoManagerStorage videoManagerStorager; @Autowired private IMediaService mediaService; @Autowired - private ZLMRESTfulUtils zlmresTfulUtils;; + private ZLMRESTfulUtils zlmresTfulUtils; @Autowired private StreamProxyMapper streamProxyMapper; @@ -55,7 +62,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService { private IRedisCatchStorage redisCatchStorage; @Autowired - private UserSetup userSetup; + private IVideoManagerStorage storager; + + @Autowired + private UserSetting userSetting; @Autowired private GbStreamMapper gbStreamMapper; @@ -63,6 +73,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Autowired private PlatformGbStreamMapper platformGbStreamMapper; + @Autowired + private EventPublisher eventPublisher; + @Autowired private ParentPlatformMapper parentPlatformMapper; @@ -72,84 +85,132 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Autowired private IMediaServerService mediaServerService; + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + @Override - public WVPResult save(StreamProxyItem param) { + public StreamInfo save(StreamProxyItem param) { MediaServerItem mediaInfo; - WVPResult wvpResult = new WVPResult<>(); - wvpResult.setCode(0); - if ("auto".equals(param.getMediaServerId())){ - mediaInfo = mediaServerService.getMediaServerForMinimumLoad(); + if (ObjectUtils.isEmpty(param.getMediaServerId()) || "auto".equals(param.getMediaServerId())){ + mediaInfo = mediaServerService.getMediaServerForMinimumLoad(null); }else { mediaInfo = mediaServerService.getOne(param.getMediaServerId()); } if (mediaInfo == null) { logger.warn("保存代理未找到在线的ZLM..."); - wvpResult.setMsg("保存失败"); - return wvpResult; + throw new ControllerException(ErrorCode.ERROR100.getCode(), "保存代理未找到在线的ZLM"); } String dstUrl = String.format("rtmp://%s:%s/%s/%s", "127.0.0.1", mediaInfo.getRtmpPort(), param.getApp(), param.getStream() ); param.setDst_url(dstUrl); - StringBuffer result = new StringBuffer(); - boolean streamLive = false; + StringBuffer resultMsg = new StringBuffer(); param.setMediaServerId(mediaInfo.getId()); boolean saveResult; // 更新 if (videoManagerStorager.queryStreamProxy(param.getApp(), param.getStream()) != null) { - saveResult = videoManagerStorager.updateStreamProxy(param); + saveResult = updateStreamProxy(param); }else { // 新增 - saveResult = videoManagerStorager.addStreamProxy(param); + saveResult = addStreamProxy(param); } - if (saveResult) { - result.append("保存成功"); - if (param.isEnable()) { - JSONObject jsonObject = addStreamProxyToZlm(param); - if (jsonObject == null || jsonObject.getInteger("code") != 0) { - streamLive = false; - result.append(", 但是启用失败,请检查流地址是否可用"); - param.setEnable(false); - // 直接移除 - if (param.isEnable_remove_none_reader()) { - del(param.getApp(), param.getStream()); - }else { - videoManagerStorager.updateStreamProxy(param); - } - + if (!saveResult) { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"保存失败"); + } + StreamInfo resultForStreamInfo = null; + resultMsg.append("保存成功"); + if (param.isEnable()) { + JSONObject jsonObject = addStreamProxyToZlm(param); + if (jsonObject == null || jsonObject.getInteger("code") != 0) { + resultMsg.append(", 但是启用失败,请检查流地址是否可用"); + param.setEnable(false); + // 直接移除 + if (param.isEnable_remove_none_reader()) { + del(param.getApp(), param.getStream()); }else { - streamLive = true; - StreamInfo streamInfo = mediaService.getStreamInfoByAppAndStream( - mediaInfo, param.getApp(), param.getStream(), null); - wvpResult.setData(streamInfo); + updateStreamProxy(param); + } - } - } - }else { - result.append("保存失败"); - } - if ( !StringUtils.isEmpty(param.getPlatformGbId()) && streamLive) { - List gbStreams = new ArrayList<>(); - gbStreams.add(param); - if (gbStreamService.addPlatformInfo(gbStreams, param.getPlatformGbId())){ - result.append(", 关联国标平台[ " + param.getPlatformGbId() + " ]成功"); }else { - result.append(", 关联国标平台[ " + param.getPlatformGbId() + " ]失败"); + resultForStreamInfo = mediaService.getStreamInfoByAppAndStream( + mediaInfo, param.getApp(), param.getStream(), null, null); + } } - // 查找开启了全部直播流共享的上级平台 - List parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream(); - if (parentPlatforms.size() > 0) { - for (ParentPlatform parentPlatform : parentPlatforms) { - param.setPlatformId(parentPlatform.getServerGBId()); - String stream = param.getStream(); - StreamProxyItem streamProxyItems = platformGbStreamMapper.selectOne(param.getApp(), stream, parentPlatform.getServerGBId()); - if (streamProxyItems == null) { - platformGbStreamMapper.add(param); + return resultForStreamInfo; + } + + /** + * 新增代理流 + * @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); } - wvpResult.setMsg(result.toString()); - return wvpResult; + + + 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) { + e.printStackTrace(); + dataSourceTransactionManager.rollback(transactionStatus); + } + return result; } @Override @@ -167,10 +228,10 @@ public class StreamProxyServiceImpl implements IStreamProxyService { } if ("default".equals(param.getType())){ result = zlmresTfulUtils.addStreamProxy(mediaServerItem, param.getApp(), param.getStream(), param.getUrl(), - param.isEnable_hls(), param.isEnable_mp4(), param.getRtp_type()); + param.isEnable_audio(), param.isEnable_mp4(), param.getRtp_type()); }else if ("ffmpeg".equals(param.getType())) { result = zlmresTfulUtils.addFFmpegSource(mediaServerItem, param.getSrc_url(), param.getDst_url(), - param.getTimeout_ms() + "", param.isEnable_hls(), param.isEnable_mp4(), + param.getTimeout_ms() + "", param.isEnable_audio(), param.isEnable_mp4(), param.getFfmpeg_cmd_key()); } return result; @@ -178,7 +239,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Override public JSONObject removeStreamProxyFromZlm(StreamProxyItem param) { - if (param ==null) return null; + if (param ==null) { + return null; + } MediaServerItem mediaServerItem = mediaServerService.getOne(param.getMediaServerId()); JSONObject result = zlmresTfulUtils.closeStreams(mediaServerItem, param.getApp(), param.getStream()); return result; @@ -193,6 +256,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { public void del(String app, String stream) { StreamProxyItem streamProxyItem = videoManagerStorager.queryStreamProxy(app, stream); if (streamProxyItem != null) { + gbStreamService.sendCatalogMsg(streamProxyItem, CatalogEvent.DEL); videoManagerStorager.deleteStreamProxy(app, stream); JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyItem); if (jsonObject != null && jsonObject.getInteger("code") == 0) { @@ -211,13 +275,18 @@ public class StreamProxyServiceImpl implements IStreamProxyService { public boolean start(String app, String stream) { boolean result = false; StreamProxyItem streamProxy = videoManagerStorager.queryStreamProxy(app, stream); - if (!streamProxy.isEnable() && streamProxy != null) { + if (streamProxy != null && !streamProxy.isEnable() ) { JSONObject jsonObject = addStreamProxyToZlm(streamProxy); - if (jsonObject == null) return false; + if (jsonObject == null) { + return false; + } if (jsonObject.getInteger("code") == 0) { result = true; streamProxy.setEnable(true); - videoManagerStorager.updateStreamProxy(streamProxy); + updateStreamProxy(streamProxy); + }else { + logger.info("启用代理失败: {}/{}->{}({})", app, stream, jsonObject.getString("msg"), + streamProxy.getSrc_url() == null? streamProxy.getUrl():streamProxy.getSrc_url()); } } return result; @@ -229,9 +298,9 @@ public class StreamProxyServiceImpl implements IStreamProxyService { StreamProxyItem streamProxyDto = videoManagerStorager.queryStreamProxy(app, stream); if (streamProxyDto != null && streamProxyDto.isEnable()) { JSONObject jsonObject = removeStreamProxyFromZlm(streamProxyDto); - if (jsonObject.getInteger("code") == 0) { + if (jsonObject != null && jsonObject.getInteger("code") == 0) { streamProxyDto.setEnable(false); - result = videoManagerStorager.updateStreamProxy(streamProxyDto); + result = updateStreamProxy(streamProxyDto); } } return result; @@ -262,7 +331,30 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Override public void zlmServerOnline(String mediaServerId) { - zlmServerOffline(mediaServerId); + // 移除开启了无人观看自动移除的流 + List streamProxyItemList = streamProxyMapper.selecAutoRemoveItemByMediaServerId(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()); + JSONObject jsonObject = addStreamProxyToZlm(streamProxyDto); + if (jsonObject == null) { + // 设置为离线 + logger.info("恢复流代理失败" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream()); + updateStatus(false, streamProxyDto.getApp(), streamProxyDto.getStream()); + }else { + updateStatus(true, streamProxyDto.getApp(), streamProxyDto.getStream()); + } + } } @Override @@ -273,23 +365,23 @@ public class StreamProxyServiceImpl implements IStreamProxyService { gbStreamMapper.batchDel(streamProxyItemList); } streamProxyMapper.deleteAutoRemoveItemByMediaServerId(mediaServerId); - // 其他的流设置未启用 - streamProxyMapper.updateStatus(false, mediaServerId); + // 其他的流设置离线 + streamProxyMapper.updateStatusByMediaServerId(mediaServerId, false); String type = "PULL"; // 发送redis消息 - List streamInfoList = redisCatchStorage.getStreams(mediaServerId, type); - if (streamInfoList.size() > 0) { - for (StreamInfo streamInfo : streamInfoList) { + List onStreamChangedHookParams = redisCatchStorage.getStreams(mediaServerId, type); + if (onStreamChangedHookParams.size() > 0) { + for (OnStreamChangedHookParam onStreamChangedHookParam : onStreamChangedHookParams) { JSONObject jsonObject = new JSONObject(); - jsonObject.put("serverId", userSetup.getServerId()); - jsonObject.put("app", streamInfo.getApp()); - jsonObject.put("stream", streamInfo.getStreamId()); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", onStreamChangedHookParam.getApp()); + jsonObject.put("stream", onStreamChangedHookParam.getStream()); jsonObject.put("register", false); jsonObject.put("mediaServerId", mediaServerId); redisCatchStorage.sendStreamChangeMsg(type, jsonObject); // 移除redis内流的信息 - redisCatchStorage.removeStream(mediaServerId, type, streamInfo.getApp(), streamInfo.getStreamId()); + redisCatchStorage.removeStream(mediaServerId, type, onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream()); } } } @@ -298,4 +390,55 @@ public class StreamProxyServiceImpl implements IStreamProxyService { public void clean() { } + + @Override + public int updateStatus(boolean status, String app, String stream) { + return streamProxyMapper.updateStatus(app, stream, status); + } + + private void syncPullStream(String mediaServerId){ + MediaServerItem mediaServer = mediaServerService.getOne(mediaServerId); + if (mediaServer != null) { + List allPullStream = redisCatchStorage.getStreams(mediaServerId, "PULL"); + if (allPullStream.size() > 0) { + zlmresTfulUtils.getMediaList(mediaServer, jsonObject->{ + Map stringStreamInfoMap = new HashMap<>(); + if (jsonObject.getInteger("code") == 0) { + JSONArray data = jsonObject.getJSONArray("data"); + if(data != null && data.size() > 0) { + for (int i = 0; i < data.size(); i++) { + JSONObject streamJSONObj = data.getJSONObject(i); + if ("rtsp".equals(streamJSONObj.getString("schema"))) { + StreamInfo streamInfo = new StreamInfo(); + String app = streamJSONObj.getString("app"); + String stream = streamJSONObj.getString("stream"); + streamInfo.setApp(app); + streamInfo.setStream(stream); + stringStreamInfoMap.put(app+stream, streamInfo); + } + } + } + } + if (stringStreamInfoMap.size() == 0) { + 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 ResourceBaceInfo getOverview() { + return streamProxyMapper.getOverview(); + } } 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 index 6dfec546..b98f1886 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushServiceImpl.java @@ -1,46 +1,69 @@ package com.genersoft.iot.vmp.service.impl; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.TypeReference; -import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookSubscribe; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.genersoft.iot.vmp.conf.MediaConfig; +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.ZLMRESTfulUtils; -import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig; import com.genersoft.iot.vmp.media.zlm.dto.*; +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.IMediaServerService; 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.GbStreamMapper; -import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper; -import com.genersoft.iot.vmp.storager.dao.PlatformGbStreamMapper; -import com.genersoft.iot.vmp.storager.dao.StreamPushMapper; +import com.genersoft.iot.vmp.storager.dao.*; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; 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.util.StringUtils; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.util.ObjectUtils; import java.util.*; +import java.util.stream.Collectors; @Service 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 ZLMRESTfulUtils zlmresTfulUtils; @@ -48,19 +71,31 @@ public class StreamPushServiceImpl implements IStreamPushService { private IRedisCatchStorage redisCatchStorage; @Autowired - private UserSetup userSetup; + private UserSetting userSetting; @Autowired private IMediaServerService mediaServerService; + @Autowired + DataSourceTransactionManager dataSourceTransactionManager; + + @Autowired + TransactionDefinition transactionDefinition; + + @Autowired + private MediaConfig mediaConfig; + + @Override public List handleJSON(String jsonData, MediaServerItem mediaServerItem) { - if (jsonData == null) return null; + if (jsonData == null) { + return null; + } Map result = new HashMap<>(); - List mediaItems = JSON.parseObject(jsonData, new TypeReference>() {}); - for (MediaItem item : mediaItems) { + List onStreamChangedHookParams = JSON.parseObject(jsonData, new TypeReference>() {}); + for (OnStreamChangedHookParam item : onStreamChangedHookParams) { // 不保存国标推理以及拉流代理的流 if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal() @@ -73,71 +108,69 @@ public class StreamPushServiceImpl implements IStreamPushService { result.put(key, streamPushItem); } } - } return new ArrayList<>(result.values()); } @Override - public StreamPushItem transform(MediaItem item) { + 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.setCreateStamp(item.getCreateStamp()); streamPushItem.setOriginSock(item.getOriginSock()); streamPushItem.setTotalReaderCount(item.getTotalReaderCount()); streamPushItem.setOriginType(item.getOriginType()); streamPushItem.setOriginTypeStr(item.getOriginTypeStr()); streamPushItem.setOriginUrl(item.getOriginUrl()); - streamPushItem.setCreateStamp(item.getCreateStamp()); + 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) { + public PageInfo getPushList(Integer page, Integer count, String query, Boolean pushing, String mediaServerId) { PageHelper.startPage(page, count); - List all = streamPushMapper.selectAll(); + List all = streamPushMapper.selectAllForList(query, pushing, mediaServerId); return new PageInfo<>(all); } @Override public List getPushList(String mediaServerId) { - return streamPushMapper.selectAllByMediaServerId(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); - // 查找开启了全部直播流共享的上级平台 - List parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream(); - if (parentPlatforms.size() > 0) { - for (ParentPlatform parentPlatform : parentPlatforms) { - stream.setPlatformId(parentPlatform.getServerGBId()); - String streamId = stream.getStream(); - StreamProxyItem streamProxyItems = platformGbStreamMapper.selectOne(stream.getApp(), streamId, parentPlatform.getServerGBId()); - if (streamProxyItems == null) { - platformGbStreamMapper.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()); MediaServerItem mediaInfo = mediaServerService.getOne(stream.getMediaServerId()); JSONObject mediaList = zlmresTfulUtils.getMediaList(mediaInfo, stream.getApp(), stream.getStream()); - if (mediaList == null) { - streamPushMapper.del(stream.getApp(), stream.getStream()); + if (mediaList != null) { + if (mediaList.getInteger("code") == 0) { + JSONArray data = mediaList.getJSONArray("data"); + if (data == null) { + streamPushMapper.del(stream.getApp(), stream.getStream()); + } + } } return del > 0; } @@ -145,16 +178,19 @@ public class StreamPushServiceImpl implements IStreamPushService { @Override public StreamPushItem getPush(String app, String streamId) { - return streamPushMapper.selectOne(app, streamId); } @Override public boolean stop(String app, String streamId) { StreamPushItem streamPushItem = streamPushMapper.selectOne(app, streamId); - int delStream = streamPushMapper.del(app, streamId); - gbStreamMapper.del(app, streamId); + if (streamPushItem != null) { + gbStreamService.sendCatalogMsg(streamPushItem, CatalogEvent.DEL); + } + platformGbStreamMapper.delByAppAndStream(app, streamId); + gbStreamMapper.del(app, streamId); + int delStream = streamPushMapper.del(app, streamId); if (delStream > 0) { MediaServerItem mediaServerItem = mediaServerService.getOne(streamPushItem.getMediaServerId()); zlmresTfulUtils.closeStreams(mediaServerItem,app, streamId); @@ -173,20 +209,30 @@ public class StreamPushServiceImpl implements IStreamPushService { List pushList = getPushList(mediaServerId); Map pushItemMap = new HashMap<>(); // redis记录 - List streamInfoPushList = redisCatchStorage.getStreams(mediaServerId, "PUSH"); - Map streamInfoPushItemMap = new HashMap<>(); + List onStreamChangedHookParams = redisCatchStorage.getStreams(mediaServerId, "PUSH"); + Map streamInfoPushItemMap = new HashMap<>(); if (pushList.size() > 0) { for (StreamPushItem streamPushItem : pushList) { - pushItemMap.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem); + if (ObjectUtils.isEmpty(streamPushItem.getGbId())) { + pushItemMap.put(streamPushItem.getApp() + streamPushItem.getStream(), streamPushItem); + } } } - if (streamInfoPushList.size() > 0) { - for (StreamInfo streamInfo : streamInfoPushList) { - streamInfoPushItemMap.put(streamInfo.getApp() + streamInfo.getStreamId(), streamInfo); + if (onStreamChangedHookParams.size() > 0) { + for (OnStreamChangedHookParam onStreamChangedHookParam : onStreamChangedHookParams) { + streamInfoPushItemMap.put(onStreamChangedHookParam.getApp() + onStreamChangedHookParam.getStream(), onStreamChangedHookParam); } } + // 获取所有推流鉴权信息,清理过期的 + List allStreamAuthorityInfo = redisCatchStorage.getAllStreamAuthorityInfo(); + Map streamAuthorityInfoInfoMap = new HashMap<>(); + for (StreamAuthorityInfo streamAuthorityInfo : allStreamAuthorityInfo) { + streamAuthorityInfoInfoMap.put(streamAuthorityInfo.getApp() + streamAuthorityInfo.getStream(), streamAuthorityInfo); + } zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{ - if (mediaList == null) return; + if (mediaList == null) { + return; + } String dataStr = mediaList.getString("data"); Integer code = mediaList.getInteger("code"); @@ -201,6 +247,7 @@ public class StreamPushServiceImpl implements IStreamPushService { 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()); @@ -221,19 +268,27 @@ public class StreamPushServiceImpl implements IStreamPushService { } } - Collection offlineStreamInfoItems = streamInfoPushItemMap.values(); - if (offlineStreamInfoItems.size() > 0) { + Collection offlineOnStreamChangedHookParamList = streamInfoPushItemMap.values(); + if (offlineOnStreamChangedHookParamList.size() > 0) { String type = "PUSH"; - for (StreamInfo offlineStreamInfoItem : offlineStreamInfoItems) { + for (OnStreamChangedHookParam offlineOnStreamChangedHookParam : offlineOnStreamChangedHookParamList) { JSONObject jsonObject = new JSONObject(); - jsonObject.put("serverId", userSetup.getServerId()); - jsonObject.put("app", offlineStreamInfoItem.getApp()); - jsonObject.put("stream", offlineStreamInfoItem.getStreamId()); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", offlineOnStreamChangedHookParam.getApp()); + jsonObject.put("stream", offlineOnStreamChangedHookParam.getStream()); jsonObject.put("register", false); jsonObject.put("mediaServerId", mediaServerId); redisCatchStorage.sendStreamChangeMsg(type, jsonObject); // 移除redis内流的信息 - redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlineStreamInfoItem.getApp(), offlineStreamInfoItem.getStreamId()); + redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", offlineOnStreamChangedHookParam.getApp(), offlineOnStreamChangedHookParam.getStream()); + } + } + + Collection streamAuthorityInfos = streamAuthorityInfoInfoMap.values(); + if (streamAuthorityInfos.size() > 0) { + for (StreamAuthorityInfo streamAuthorityInfo : streamAuthorityInfos) { + // 移除redis内流的信息 + redisCatchStorage.removeStreamAuthorityInfo(streamAuthorityInfo.getApp(), streamAuthorityInfo.getStream()); } } })); @@ -241,24 +296,25 @@ public class StreamPushServiceImpl implements IStreamPushService { @Override public void zlmServerOffline(String mediaServerId) { - List streamPushItems = streamPushMapper.selectAllByMediaServerId(mediaServerId); + List streamPushItems = streamPushMapper.selectAllByMediaServerIdWithOutGbID(mediaServerId); // 移除没有GBId的推流 streamPushMapper.deleteWithoutGBId(mediaServerId); gbStreamMapper.deleteWithoutGBId("push", mediaServerId); // 其他的流设置未启用 - gbStreamMapper.updateStatusByMediaServerId(mediaServerId, false); + streamPushMapper.updateStatusByMediaServerId(mediaServerId, false); + streamProxyMapper.updateStatusByMediaServerId(mediaServerId, false); // 发送流停止消息 String type = "PUSH"; // 发送redis消息 - List streamInfoList = redisCatchStorage.getStreams(mediaServerId, type); + List streamInfoList = redisCatchStorage.getStreams(mediaServerId, type); if (streamInfoList.size() > 0) { - for (StreamInfo streamInfo : streamInfoList) { + for (OnStreamChangedHookParam onStreamChangedHookParam : streamInfoList) { // 移除redis内流的信息 - redisCatchStorage.removeStream(mediaServerId, type, streamInfo.getApp(), streamInfo.getStreamId()); + redisCatchStorage.removeStream(mediaServerId, type, onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream()); JSONObject jsonObject = new JSONObject(); - jsonObject.put("serverId", userSetup.getServerId()); - jsonObject.put("app", streamInfo.getApp()); - jsonObject.put("stream", streamInfo.getStreamId()); + jsonObject.put("serverId", userSetting.getServerId()); + jsonObject.put("app", onStreamChangedHookParam.getApp()); + jsonObject.put("stream", onStreamChangedHookParam.getStream()); jsonObject.put("register", false); jsonObject.put("mediaServerId", mediaServerId); redisCatchStorage.sendStreamChangeMsg(type, jsonObject); @@ -279,6 +335,7 @@ public class StreamPushServiceImpl implements IStreamPushService { streamPushItem.setStreamType("push"); streamPushItem.setStatus(true); streamPushItem.setGbId("34020000004111" + gbId); + streamPushItem.setCreateTime(DateUtil.getNow()); gbId ++; } int limitCount = 30; @@ -296,4 +353,185 @@ public class StreamPushServiceImpl implements IStreamPushService { } 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.getId() != 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.size() > 0) { + 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) { + MediaServerItem mediaServerItem = mediaServerService.getOne(gbStream.getMediaServerId()); + zlmresTfulUtils.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()); + + // 放在事务内执行 + 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 ResourceBaceInfo getOverview() { + return streamPushMapper.getOverview(userSetting.isUsePushingAsStatus()); + } } 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 new file mode 100644 index 00000000..1b21995b --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamPushUploadFileHandler.java @@ -0,0 +1,177 @@ +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 org.springframework.util.StringUtils; + +import java.util.*; + +public class StreamPushUploadFileHandler extends AnalysisEventListener { + + /** + * 错误数据的回调,用于将错误数据发送给页面 + */ + private ErrorDataHandler errorDataHandler; + + /** + * 推流的业务类用于存储数据 + */ + private IStreamPushService pushService; + + /** + * 默认流媒体节点ID + */ + private String defaultMediaServerId; + + /** + * 用于存储不加过滤的所有数据 + */ + private List streamPushItems = new ArrayList<>(); + + /** + * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 + */ + private Map streamPushItemForSave = new HashMap<>(); + + /** + * 用于存储按照APP+Stream为KEY, 平台ID+目录Id 为value的数据,用于存储到gb_stream表后获取app+Stream对应的平台与目录信息,然后存入关联表 + */ + private Map> streamPushItemsForPlatform = new HashMap<>(); + + /** + * 用于判断文件是否存在重复的app+Stream+平台ID + */ + private Set streamPushStreamSet = new HashSet<>(); + + /** + * 用于存储APP+Stream->国标ID 的数据结构, 数据一一对应,全局判断APP+Stream->国标ID是否存在不对应 + */ + private BiMap gBMap = HashBiMap.create(); + + /** + * 记录错误的APP+Stream + */ + private List errorStreamList = new ArrayList<>(); + + + /** + * 记录错误的国标ID + */ + private List errorGBList = new ArrayList<>(); + + /** + * 读取数量计数器 + */ + private int loadedSize = 0; + + public StreamPushUploadFileHandler(IStreamPushService pushService, String defaultMediaServerId, ErrorDataHandler errorDataHandler) { + this.pushService = pushService; + this.defaultMediaServerId = defaultMediaServerId; + this.errorDataHandler = errorDataHandler; + } + + 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; + } + + if (gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()) == null) { + try { + gBMap.put(streamPushExcelDto.getApp() + streamPushExcelDto.getStream(), streamPushExcelDto.getGbId()); + }catch (IllegalArgumentException e) { + errorGBList.add(streamPushExcelDto.getGbId() + "(不同的app+stream使用了相同的国标ID)"); + return; + } + }else { + if (!gBMap.get(streamPushExcelDto.getApp() + streamPushExcelDto.getStream()).equals(streamPushExcelDto.getGbId())) { + errorGBList.add(streamPushExcelDto.getGbId() + "(同一组app+stream使用了不同的国标ID)"); + return; + } + } + + if (streamPushStreamSet.contains(streamPushExcelDto.getApp() + streamPushExcelDto.getStream() + streamPushExcelDto.getPlatformId())) { + errorStreamList.add(streamPushExcelDto.getApp() + "/" + streamPushExcelDto.getStream()+ "/" + + streamPushExcelDto.getPlatformId() + "(同一组app+stream添加在了同一个平台下)"); + return; + }else { + 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, errorGBList); + } + + 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/impl/UserServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java index 32b6ad68..aeba9edc 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/UserServiceImpl.java @@ -3,14 +3,18 @@ package com.genersoft.iot.vmp.service.impl; import com.genersoft.iot.vmp.service.IUserService; import com.genersoft.iot.vmp.storager.dao.UserMapper; import com.genersoft.iot.vmp.storager.dao.dto.User; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; import java.util.List; @Service public class UserServiceImpl implements IUserService { - + @Autowired private UserMapper userMapper; @@ -34,7 +38,9 @@ public class UserServiceImpl implements IUserService { @Override public int addUser(User user) { User userByUsername = userMapper.getUserByUsername(user.getUsername()); - if (userByUsername != null) return 0; + if (userByUsername != null) { + return 0; + } return userMapper.add(user); } @Override @@ -53,4 +59,24 @@ public class UserServiceImpl implements IUserService { } + @Override + public boolean checkPushAuthority(String callId, String sign) { + if (ObjectUtils.isEmpty(callId)) { + return userMapper.checkPushAuthorityByCallId(sign).size() > 0; + }else { + return userMapper.checkPushAuthorityByCallIdAndSign(callId, sign).size() > 0; + } + } + + @Override + public PageInfo getUsers(int page, int count) { + PageHelper.startPage(page, count); + List users = userMapper.getUsers(); + return new PageInfo<>(users); + } + + @Override + public int changePushKey(int id, String pushKey) { + return userMapper.changePushKey(id,pushKey); + } } 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 new file mode 100644 index 00000000..9bb3bbd2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java @@ -0,0 +1,114 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.gb28181.bean.*; +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.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 org.springframework.util.ObjectUtils; + +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import javax.validation.constraints.NotNull; +import java.text.ParseException; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + + +@Component +public class RedisAlarmMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisAlarmMsgListener.class); + + @Autowired + private ISIPCommander commander; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Autowired + private IVideoManagerStorage storage; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void onMessage(@NotNull Message message, byte[] bytes) { + logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody())); + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize()); + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + AlarmChannelMessage alarmChannelMessage = JSON.parseObject(msg.getBody(), AlarmChannelMessage.class); + if (alarmChannelMessage == null) { + logger.warn("[REDIS的ALARM通知]消息解析失败"); + continue; + } + String gbId = alarmChannelMessage.getGbId(); + + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setCreateTime(DateUtil.getNow()); + deviceAlarm.setChannelId(gbId); + deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription()); + deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn()); + deviceAlarm.setAlarmPriority("1"); + deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601()); + deviceAlarm.setAlarmType("1"); + deviceAlarm.setLongitude(0); + deviceAlarm.setLatitude(0); + + if (ObjectUtils.isEmpty(gbId)) { + // 发送给所有的上级 + List parentPlatforms = storage.queryEnableParentPlatformList(true); + if (parentPlatforms.size() > 0) { + for (ParentPlatform parentPlatform : parentPlatforms) { + try { + commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); + } + } + } + }else { + Device device = storage.queryVideoDevice(gbId); + ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId); + if (device != null && platform == null) { + try { + commander.sendAlarmMessage(device, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 发送报警: {}", e.getMessage()); + } + }else if (device == null && platform != null){ + try { + commanderForPlatform.sendAlarmMessage(platform, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 发送报警: {}", e.getMessage()); + } + }else { + logger.warn("无法确定" + gbId + "是平台还是设备"); + } + } + }catch (Exception e) { + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + } + } + }); + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java new file mode 100644 index 00000000..35ed99e4 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java @@ -0,0 +1,393 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.DynamicTask; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; +import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory; +import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange; +import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.IMediaServerService; +import com.genersoft.iot.vmp.service.bean.*; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.utils.redis.RedisUtil; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import org.slf4j.Logger; +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 java.text.ParseException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + + +/** + * 监听下级发送推送信息,并发送国标推流消息上级 + * @author lin + */ +@Component +public class RedisGbPlayMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisGbPlayMsgListener.class); + + public static final String WVP_PUSH_STREAM_KEY = "WVP_PUSH_STREAM"; + + /** + * 流媒体不存在的错误玛 + */ + public static final int ERROR_CODE_MEDIA_SERVER_NOT_FOUND = -1; + + /** + * 离线的错误玛 + */ + public static final int ERROR_CODE_OFFLINE = -2; + + /** + * 超时的错误玛 + */ + public static final int ERROR_CODE_TIMEOUT = -3; + + private Map callbacks = new ConcurrentHashMap<>(); + private Map callbacksForStartSendRtpStream = new ConcurrentHashMap<>(); + private Map callbacksForError = new ConcurrentHashMap<>(); + + @Autowired + private UserSetting userSetting; + + + @Autowired + private ZLMMediaListManager zlmMediaListManager; + + @Autowired + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private DynamicTask dynamicTask; + + @Autowired + private ZLMMediaListManager mediaListManager; + + @Autowired + private ZlmHttpHookSubscribe subscribe; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + + public interface PlayMsgCallback{ + void handler(ResponseSendItemMsg responseSendItemMsg) throws ParseException; + } + + public interface PlayMsgCallbackForStartSendRtpStream{ + void handler(JSONObject jsonObject); + } + + public interface PlayMsgErrorCallback{ + void handler(WVPResult wvpResult); + } + + @Override + public void onMessage(Message message, byte[] bytes) { + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + JSONObject msgJSON = JSON.parseObject(msg.getBody(), JSONObject.class); + WvpRedisMsg wvpRedisMsg = JSON.to(WvpRedisMsg.class, msgJSON); + if (!userSetting.getServerId().equals(wvpRedisMsg.getToId())) { + continue; + } + if (WvpRedisMsg.isRequest(wvpRedisMsg)) { + logger.info("[收到REDIS通知] 请求: {}", new String(msg.getBody())); + + switch (wvpRedisMsg.getCmd()){ + case WvpRedisMsgCmd.GET_SEND_ITEM: + RequestSendItemMsg content = JSON.to(RequestSendItemMsg.class, wvpRedisMsg.getContent()); + requestSendItemMsgHand(content, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial()); + break; + case WvpRedisMsgCmd.REQUEST_PUSH_STREAM: + RequestPushStreamMsg param = JSON.to(RequestPushStreamMsg.class, wvpRedisMsg.getContent()); + requestPushStreamMsgHand(param, wvpRedisMsg.getFromId(), wvpRedisMsg.getSerial()); + break; + default: + break; + } + + }else { + logger.info("[收到REDIS通知] 回复: {}", new String(msg.getBody())); + switch (wvpRedisMsg.getCmd()){ + case WvpRedisMsgCmd.GET_SEND_ITEM: + + WVPResult content = JSON.to(WVPResult.class, wvpRedisMsg.getContent()); + + String key = wvpRedisMsg.getSerial(); + switch (content.getCode()) { + case 0: + ResponseSendItemMsg responseSendItemMsg =JSON.to(ResponseSendItemMsg.class, content.getData()); + PlayMsgCallback playMsgCallback = callbacks.get(key); + if (playMsgCallback != null) { + callbacksForError.remove(key); + try { + playMsgCallback.handler(responseSendItemMsg); + } catch (ParseException e) { + logger.error("[REDIS消息处理异常] ", e); + } + } + break; + case ERROR_CODE_MEDIA_SERVER_NOT_FOUND: + case ERROR_CODE_OFFLINE: + case ERROR_CODE_TIMEOUT: + PlayMsgErrorCallback errorCallback = callbacksForError.get(key); + if (errorCallback != null) { + callbacks.remove(key); + errorCallback.handler(content); + } + break; + default: + break; + } + break; + case WvpRedisMsgCmd.REQUEST_PUSH_STREAM: + WVPResult wvpResult = JSON.to(WVPResult.class, wvpRedisMsg.getContent()); + String serial = wvpRedisMsg.getSerial(); + switch (wvpResult.getCode()) { + case 0: + JSONObject jsonObject = (JSONObject)wvpResult.getData(); + PlayMsgCallbackForStartSendRtpStream playMsgCallback = callbacksForStartSendRtpStream.get(serial); + if (playMsgCallback != null) { + callbacksForError.remove(serial); + playMsgCallback.handler(jsonObject); + } + break; + case ERROR_CODE_MEDIA_SERVER_NOT_FOUND: + case ERROR_CODE_OFFLINE: + case ERROR_CODE_TIMEOUT: + PlayMsgErrorCallback errorCallback = callbacksForError.get(serial); + if (errorCallback != null) { + callbacks.remove(serial); + errorCallback.handler(wvpResult); + } + break; + default: + break; + } + break; + default: + break; + } + + } + }catch (Exception e) { + logger.warn("[RedisGbPlayMsg] 发现未处理的异常, {}",e.getMessage()); + } + } + }); + } + } + + /** + * 处理收到的请求推流的请求 + */ + private void requestPushStreamMsgHand(RequestPushStreamMsg requestPushStreamMsg, String fromId, String serial) { + MediaServerItem mediaInfo = mediaServerService.getOne(requestPushStreamMsg.getMediaServerId()); + if (mediaInfo == null) { + // TODO 回复错误 + return; + } + String is_Udp = requestPushStreamMsg.isTcp() ? "0" : "1"; + Map param = new HashMap<>(); + param.put("vhost","__defaultVhost__"); + param.put("app",requestPushStreamMsg.getApp()); + param.put("stream",requestPushStreamMsg.getStream()); + param.put("ssrc", requestPushStreamMsg.getSsrc()); + param.put("dst_url",requestPushStreamMsg.getIp()); + param.put("dst_port", requestPushStreamMsg.getPort()); + param.put("is_udp", is_Udp); + param.put("src_port", requestPushStreamMsg.getSrcPort()); + param.put("pt", requestPushStreamMsg.getPt()); + param.put("use_ps", requestPushStreamMsg.isPs() ? "1" : "0"); + param.put("only_audio", requestPushStreamMsg.isOnlyAudio() ? "1" : "0"); + JSONObject jsonObject = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param); + // 回复消息 + responsePushStream(jsonObject, fromId, serial); + } + + private void responsePushStream(JSONObject content, String toId, String serial) { + + WVPResult result = new WVPResult<>(); + result.setCode(0); + result.setData(content); + + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId, + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, serial, result); + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); + RedisUtil.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + } + + /** + * 处理收到的请求sendItem的请求 + */ + private void requestSendItemMsgHand(RequestSendItemMsg content, String toId, String serial) { + MediaServerItem mediaServerItem = mediaServerService.getOne(content.getMediaServerId()); + if (mediaServerItem == null) { + logger.info("[回复推流信息] 流媒体{}不存在 ", content.getMediaServerId()); + + WVPResult result = new WVPResult<>(); + result.setCode(ERROR_CODE_MEDIA_SERVER_NOT_FOUND); + result.setMsg("流媒体不存在"); + + WvpRedisMsg response = WvpRedisMsg.getResponseInstance(userSetting.getServerId(), toId, + WvpRedisMsgCmd.GET_SEND_ITEM, serial, result); + + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); + RedisUtil.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + return; + } + // 确定流是否在线 + boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, content.getApp(), content.getStream()); + if (streamReady) { + logger.info("[回复推流信息] {}/{}", content.getApp(), content.getStream()); + responseSendItem(mediaServerItem, content, toId, serial); + }else { + // 流已经离线 + // 发送redis消息以使设备上线 + logger.info("[ app={}, stream={} ]通道离线,发送redis信息控制设备开始推流",content.getApp(), content.getStream()); + + String taskKey = UUID.randomUUID().toString(); + // 设置超时 + dynamicTask.startDelay(taskKey, ()->{ + logger.info("[ app={}, stream={} ] 等待设备开始推流超时", content.getApp(), content.getStream()); + WVPResult result = new WVPResult<>(); + result.setCode(ERROR_CODE_TIMEOUT); + WvpRedisMsg response = WvpRedisMsg.getResponseInstance( + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result + ); + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); + RedisUtil.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + }, userSetting.getPlatformPlayTimeout()); + + // 添加订阅 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(content.getApp(), content.getStream(), true, "rtsp", mediaServerItem.getId()); + + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json)->{ + dynamicTask.stop(taskKey); + responseSendItem(mediaServerItem, content, toId, serial); + }); + + MessageForPushChannel messageForPushChannel = MessageForPushChannel.getInstance(1, content.getApp(), content.getStream(), + content.getChannelId(), content.getPlatformId(), content.getPlatformName(), content.getServerId(), + content.getMediaServerId()); + redisCatchStorage.sendStreamPushRequestedMsg(messageForPushChannel); + + } + } + + /** + * 将获取到的sendItem发送出去 + */ + private void responseSendItem(MediaServerItem mediaServerItem, RequestSendItemMsg content, String toId, String serial) { + SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, content.getIp(), + content.getPort(), content.getSsrc(), content.getPlatformId(), + content.getApp(), content.getStream(), content.getChannelId(), + content.getTcp(), content.getRtcp()); + + WVPResult result = new WVPResult<>(); + result.setCode(0); + ResponseSendItemMsg responseSendItemMsg = new ResponseSendItemMsg(); + responseSendItemMsg.setSendRtpItem(sendRtpItem); + responseSendItemMsg.setMediaServerItem(mediaServerItem); + result.setData(responseSendItemMsg); + + WvpRedisMsg response = WvpRedisMsg.getResponseInstance( + userSetting.getServerId(), toId, WvpRedisMsgCmd.GET_SEND_ITEM, serial, result + ); + JSONObject jsonObject = (JSONObject)JSON.toJSON(response); + RedisUtil.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + } + + /** + * 发送消息要求下级生成推流信息 + * @param serverId 下级服务ID + * @param app 应用名 + * @param stream 流ID + * @param ip 目标IP + * @param port 目标端口 + * @param ssrc ssrc + * @param platformId 平台国标编号 + * @param channelId 通道ID + * @param isTcp 是否使用TCP + * @param callback 得到信息的回调 + */ + public void sendMsg(String serverId, String mediaServerId, String app, String stream, String ip, int port, String ssrc, + String platformId, String channelId, boolean isTcp, boolean rtcp, String platformName, PlayMsgCallback callback, PlayMsgErrorCallback errorCallback) { + RequestSendItemMsg requestSendItemMsg = RequestSendItemMsg.getInstance( + serverId, mediaServerId, app, stream, ip, port, ssrc, platformId, channelId, isTcp, rtcp, platformName); + requestSendItemMsg.setServerId(serverId); + String key = UUID.randomUUID().toString(); + WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, WvpRedisMsgCmd.GET_SEND_ITEM, + key, requestSendItemMsg); + + JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg); + logger.info("[请求推流SendItem] {}: {}", serverId, jsonObject); + callbacks.put(key, callback); + callbacksForError.put(key, errorCallback); + dynamicTask.startDelay(key, ()->{ + callbacks.remove(key); + callbacksForError.remove(key); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ERROR_CODE_TIMEOUT); + wvpResult.setMsg("timeout"); + errorCallback.handler(wvpResult); + }, userSetting.getPlatformPlayTimeout()); + RedisUtil.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + } + + /** + * 发送请求推流的消息 + * @param param 推流参数 + * @param callback 回调 + */ + public void sendMsgForStartSendRtpStream(String serverId, RequestPushStreamMsg param, PlayMsgCallbackForStartSendRtpStream callback) { + String key = UUID.randomUUID().toString(); + WvpRedisMsg redisMsg = WvpRedisMsg.getRequestInstance(userSetting.getServerId(), serverId, + WvpRedisMsgCmd.REQUEST_PUSH_STREAM, key, param); + + JSONObject jsonObject = (JSONObject)JSON.toJSON(redisMsg); + logger.info("[REDIS 请求其他平台推流] {}: {}", serverId, jsonObject); + dynamicTask.startDelay(key, ()->{ + callbacksForStartSendRtpStream.remove(key); + callbacksForError.remove(key); + }, userSetting.getPlatformPlayTimeout()); + callbacksForStartSendRtpStream.put(key, callback); + callbacksForError.put(key, (wvpResult)->{ + logger.info("[REDIS 请求其他平台推流] 失败: {}", wvpResult.getMsg()); + callbacksForStartSendRtpStream.remove(key); + callbacksForError.remove(key); + }); + RedisUtil.convertAndSend(WVP_PUSH_STREAM_KEY, jsonObject); + } +} 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 new file mode 100644 index 00000000..0c99707e --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java @@ -0,0 +1,77 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +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; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * 接收来自redis的GPS更新通知 + * @author lin + */ +@Component +public class RedisGpsMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisGpsMsgListener.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IVideoManagerStorage storager; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + + @Override + public void onMessage(@NotNull Message message, byte[] bytes) { + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + GPSMsgInfo gpsMsgInfo = JSON.parseObject(msg.getBody(), GPSMsgInfo.class); + // 只是放入redis缓存起来 + redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); + }catch (Exception e) { + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + } + } + }); + } + } + + /** + * 定时将经纬度更新到数据库 + */ + @Scheduled(fixedRate = 2 * 1000) //每2秒执行一次 + public void execute(){ + List gpsMsgInfo = redisCatchStorage.getAllGpsMsgInfo(); + if (gpsMsgInfo.size() > 0) { + storager.updateStreamGPS(gpsMsgInfo); + for (GPSMsgInfo msgInfo : gpsMsgInfo) { + msgInfo.setStored(true); + redisCatchStorage.updateGpsMsgInfo(msgInfo); + } + } + } +} 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 new file mode 100644 index 00000000..33eae1eb --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java @@ -0,0 +1,75 @@ +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 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 org.springframework.util.ObjectUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * 接收redis返回的推流结果 + * @author lin + */ +@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{ + void run(MessageForPushChannelResponse response); + } + + @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 { + MessageForPushChannelResponse response = JSON.parseObject(new String(msg.getBody()), MessageForPushChannelResponse.class); + if (response == null || ObjectUtils.isEmpty(response.getApp()) || ObjectUtils.isEmpty(response.getStream())){ + logger.info("[REDIS消息-请求推流结果]:参数不全"); + continue; + } + // 查看正在等待的invite消息 + if (responseEvents.get(response.getApp() + response.getStream()) != null) { + responseEvents.get(response.getApp() + response.getStream()).run(response); + } + }catch (Exception e) { + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + } + } + }); + } + } + + public void addEvent(String app, String stream, PushStreamResponseEvent callback) { + responseEvents.put(app + stream, callback); + } + + public void removeEvent(String app, String stream) { + responseEvents.remove(app + stream); + } +} 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 new file mode 100644 index 00000000..d8ed1a01 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java @@ -0,0 +1,104 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; +import com.genersoft.iot.vmp.service.IGbStreamService; +import com.genersoft.iot.vmp.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.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 用于判断是添加还是修改 + List allAppAndStream = streamPushService.getAllAppAndStream(); + + /** + * 用于存储更具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.contains(app + stream); + //不存在就添加 + if (!contains) { + 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); + } else { + //存在就只修改 name和gbId + streamPushItemForUpdate.add(streamPushItem); + } + } + if (streamPushItemForSave.size() > 0) { + + logger.info("添加{}条",streamPushItemForSave.size()); + logger.info(JSONObject.toJSONString(streamPushItemForSave)); + streamPushService.batchAdd(streamPushItemForSave); + + } + if(streamPushItemForUpdate.size()>0){ + logger.info("修改{}条",streamPushItemForUpdate.size()); + logger.info(JSONObject.toJSONString(streamPushItemForUpdate)); + gbStreamService.updateGbIdOrName(streamPushItemForUpdate); + } + }catch (Exception e) { + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + } + } + }); + } + } +} 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 new file mode 100644 index 00000000..96ff8e83 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java @@ -0,0 +1,100 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.genersoft.iot.vmp.common.VideoManagerConstants; +import com.genersoft.iot.vmp.conf.DynamicTask; +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +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 java.util.concurrent.ConcurrentLinkedQueue; + + +/** + * 接收redis发送的推流设备上线下线通知 + * @author lin + */ +@Component +public class RedisPushStreamStatusMsgListener implements MessageListener, ApplicationRunner { + + private final static Logger logger = LoggerFactory.getLogger(RedisPushStreamStatusMsgListener.class); + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @Autowired + private IStreamPushService streamPushService; + + @Autowired + private DynamicTask dynamicTask; + + + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void onMessage(Message message, byte[] bytes) { + boolean isEmpty = taskQueue.isEmpty(); + logger.warn("[REDIS消息-推流设备状态变化]: {}", new String(message.getBody())); + taskQueue.offer(message); + + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + PushStreamStatusChangeFromRedisDto statusChangeFromPushStream = JSON.parseObject(msg.getBody(), PushStreamStatusChangeFromRedisDto.class); + if (statusChangeFromPushStream == null) { + logger.warn("[REDIS消息]推流设备状态变化消息解析失败"); + continue; + } + // 取消定时任务 + dynamicTask.stop(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED); + if (statusChangeFromPushStream.isSetAllOffline()) { + // 所有设备离线 + streamPushService.allStreamOffline(); + } + if (statusChangeFromPushStream.getOfflineStreams() != null + && statusChangeFromPushStream.getOfflineStreams().size() > 0) { + // 更新部分设备离线 + streamPushService.offline(statusChangeFromPushStream.getOfflineStreams()); + } + if (statusChangeFromPushStream.getOnlineStreams() != null && + statusChangeFromPushStream.getOnlineStreams().size() > 0) { + // 更新部分设备上线 + streamPushService.online(statusChangeFromPushStream.getOnlineStreams()); + } + }catch (Exception e) { + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + } + } + }); + } + } + + @Override + public void run(ApplicationArguments args) throws Exception { + // 启动时设置所有推流通道离线,发起查询请求 + redisCatchStorage.sendStreamPushRequestedMsgForStatus(); + dynamicTask.startDelay(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED, ()->{ + logger.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线"); + // 五秒收不到请求就设置通道离线,然后通知上级离线 + streamPushService.allStreamOffline(); + }, 5000); + } + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java new file mode 100644 index 00000000..1cdc527a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java @@ -0,0 +1,91 @@ +package com.genersoft.iot.vmp.service.redisMsg; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.UserSetting; + +import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; +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 java.util.concurrent.ConcurrentLinkedQueue; + + +/** + * 接收其他wvp发送流变化通知 + * @author lin + */ +@Component +public class RedisStreamMsgListener implements MessageListener { + + private final static Logger logger = LoggerFactory.getLogger(RedisStreamMsgListener.class); + + @Autowired + private UserSetting userSetting; + + @Autowired + private ZLMMediaListManager zlmMediaListManager; + + private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + + @Qualifier("taskExecutor") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Override + public void onMessage(Message message, byte[] bytes) { + boolean isEmpty = taskQueue.isEmpty(); + taskQueue.offer(message); + if (isEmpty) { + taskExecutor.execute(() -> { + while (!taskQueue.isEmpty()) { + Message msg = taskQueue.poll(); + try { + JSONObject steamMsgJson = JSON.parseObject(msg.getBody(), JSONObject.class); + if (steamMsgJson == null) { + logger.warn("[收到redis 流变化]消息解析失败"); + continue; + } + String serverId = steamMsgJson.getString("serverId"); + + if (userSetting.getServerId().equals(serverId)) { + // 自己发送的消息忽略即可 + continue; + } + logger.info("[收到redis 流变化]: {}", new String(message.getBody())); + String app = steamMsgJson.getString("app"); + String stream = steamMsgJson.getString("stream"); + boolean register = steamMsgJson.getBoolean("register"); + String mediaServerId = steamMsgJson.getString("mediaServerId"); + OnStreamChangedHookParam onStreamChangedHookParam = new OnStreamChangedHookParam(); + onStreamChangedHookParam.setSeverId(serverId); + onStreamChangedHookParam.setApp(app); + onStreamChangedHookParam.setStream(stream); + onStreamChangedHookParam.setRegist(register); + onStreamChangedHookParam.setMediaServerId(mediaServerId); + onStreamChangedHookParam.setCreateStamp(System.currentTimeMillis()/1000); + onStreamChangedHookParam.setAliveSecond(0L); + onStreamChangedHookParam.setTotalReaderCount("0"); + onStreamChangedHookParam.setOriginType(0); + onStreamChangedHookParam.setOriginTypeStr("0"); + onStreamChangedHookParam.setOriginTypeStr("unknown"); + if (register) { + zlmMediaListManager.addPush(onStreamChangedHookParam); + }else { + zlmMediaListManager.removeMedia(app, stream); + } + }catch (Exception e) { + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + } + } + }); + } + } +} 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 68a772ef..15b6d807 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java @@ -1,13 +1,15 @@ package com.genersoft.iot.vmp.storager; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform; -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem; -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.common.SystemAllInfo; +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.media.zlm.dto.*; +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.service.bean.ThirdPartyGB; +import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; import java.util.List; import java.util.Map; @@ -17,10 +19,9 @@ public interface IRedisCatchStorage { /** * 计数器。为cseq进行计数 * - * @param method sip 方法 * @return */ - Long getCSEQ(String method); + Long getCSEQ(); /** * 开始播放时将流存入 @@ -46,17 +47,17 @@ public interface IRedisCatchStorage { StreamInfo queryPlayByStreamId(String steamId); - StreamInfo queryPlaybackByStreamId(String steamId); - StreamInfo queryPlayByDevice(String deviceId, String channelId); Map queryPlayByDeviceId(String deviceId); - boolean startPlayback(StreamInfo stream); + boolean startPlayback(StreamInfo stream, String callId); - boolean stopPlayback(StreamInfo streamInfo); + boolean stopPlayback(String deviceId, String channelId, String stream, String callId); - StreamInfo queryPlaybackByDevice(String deviceId, String code); + StreamInfo queryPlayback(String deviceId, String channelID, String stream, String callId); + + String queryPlaybackForKey(String deviceId, String channelId, String stream, String callId); void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch); @@ -64,17 +65,13 @@ public interface IRedisCatchStorage { void delPlatformCatchInfo(String platformGbId); - void updatePlatformKeepalive(ParentPlatform parentPlatform); - void delPlatformKeepalive(String platformGbId); - void updatePlatformRegister(ParentPlatform parentPlatform); - void delPlatformRegister(String platformGbId); - void updatePlatformRegisterInfo(String callId, String platformGbId); + void updatePlatformRegisterInfo(String callId, PlatformRegisterInfo platformRegisterInfo); - String queryPlatformRegisterInfo(String callId); + PlatformRegisterInfo queryPlatformRegisterInfo(String callId); void delPlatformRegisterInfo(String callId); @@ -88,7 +85,7 @@ public interface IRedisCatchStorage { * @param channelId * @return sendRtpItem */ - SendRtpItem querySendRTPServer(String platformGbId, String channelId); + SendRtpItem querySendRTPServer(String platformGbId, String channelId, String streamId, String callId); List querySendRTPServer(String platformGbId); @@ -97,7 +94,7 @@ public interface IRedisCatchStorage { * @param platformGbId * @param channelId */ - void deleteSendRTPServer(String platformGbId, String channelId); + void deleteSendRTPServer(String platformGbId, String channelId, String callId, String streamId); /** * 查询某个通道是否存在上级点播(RTP推送) @@ -111,23 +108,6 @@ public interface IRedisCatchStorage { */ void clearCatchByDeviceId(String deviceId); - /** - * 获取mediaServer节点 - * @param mediaServerId - * @return - */ -// MediaServerItem getMediaInfo(String mediaServerId); - - /** - * 设置所有设备离线 - */ - void outlineForAll(); - - /** - * 获取所有在线的 - */ - List getOnlineForAll(); - /** * 在redis添加wvp的信息 */ @@ -139,13 +119,19 @@ public interface IRedisCatchStorage { */ void sendStreamChangeMsg(String type, JSONObject jsonObject); + /** + * 发送报警消息 + * @param msg 消息内容 + */ + void sendAlarmMsg(AlarmChannelMessage msg); + /** * 添加流信息到redis * @param mediaServerItem * @param app * @param streamId */ - void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, StreamInfo streamInfo); + void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, OnStreamChangedHookParam item); /** * 移除流信息从redis @@ -166,9 +152,11 @@ public interface IRedisCatchStorage { * 开始下载录像时存入 * @param streamInfo */ - boolean startDownload(StreamInfo streamInfo); + boolean startDownload(StreamInfo streamInfo, String callId); - StreamInfo queryDownloadByStreamId(String streamId); + StreamInfo queryDownload(String deviceId, String channelId, String stream, String callId); + + boolean stopDownload(String deviceId, String channelId, String stream, String callId); /** * 查找第三方系统留下的国标预设值 @@ -177,7 +165,7 @@ public interface IRedisCatchStorage { */ ThirdPartyGB queryMemberNoGBId(String queryKey); - List getStreams(String mediaServerId, String pull); + List getStreams(String mediaServerId, String pull); /** * 将device信息写入redis @@ -185,10 +173,89 @@ public interface IRedisCatchStorage { */ void updateDevice(Device device); + void removeDevice(String deviceId); + /** * 获取Device */ Device getDevice(String deviceId); void resetAllCSEQ(); + + void updateGpsMsgInfo(GPSMsgInfo gpsMsgInfo); + + GPSMsgInfo getGpsMsgInfo(String gbId); + List getAllGpsMsgInfo(); + + Long getSN(String method); + + void resetAllSN(); + + OnStreamChangedHookParam getStreamInfo(String app, String streamId, String mediaServerId); + + void addCpuInfo(double cpuInfo); + + void addMemInfo(double memInfo); + + void addNetInfo(Map networkInterfaces); + + void sendMobilePositionMsg(JSONObject jsonObject); + + void sendStreamPushRequestedMsg(MessageForPushChannel messageForPushChannel); + + /** + * 判断设备状态 + * @param deviceId 设备ID + * @return + */ + public boolean deviceIsOnline(String deviceId); + + /** + * 存储推流的鉴权信息 + * @param app 应用名 + * @param stream 流 + * @param streamAuthorityInfo 鉴权信息 + */ + void updateStreamAuthorityInfo(String app, String stream, StreamAuthorityInfo streamAuthorityInfo); + + /** + * 移除推流的鉴权信息 + * @param app 应用名 + * @param streamId 流 + */ + void removeStreamAuthorityInfo(String app, String streamId); + + /** + * 获取推流的鉴权信息 + * @param app 应用名 + * @param stream 流 + * @return + */ + StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream); + + List getAllStreamAuthorityInfo(); + + /** + * 发送redis消息 查询所有推流设备的状态 + */ + void sendStreamPushRequestedMsgForStatus(); + + List querySendRTPServerByChnnelId(String channelId); + + List querySendRTPServerByStream(String stream); + + SystemAllInfo getSystemInfo(); + + int getPushStreamCount(String id); + + int getProxyStreamCount(String id); + + int getGbReceiveCount(String id); + + int getGbSendCount(String id); + + void addDiskInfo(List> diskInfo); + + List queryAllSendRTPServer(); + } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java similarity index 63% rename from src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java rename to src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java index 18f30a55..0b0a7d91 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorager.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java @@ -1,426 +1,377 @@ -package com.genersoft.iot.vmp.storager; - -import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -import com.github.pagehelper.PageInfo; - -import java.util.List; - -/** - * @description:视频设备数据存储接口 - * @author: swwheihei - * @date: 2020年5月6日 下午2:14:31 - */ -@SuppressWarnings("rawtypes") -public interface IVideoManagerStorager { - - /** - * 根据设备ID判断设备是否存在 - * - * @param deviceId 设备ID - * @return true:存在 false:不存在 - */ - public boolean exists(String deviceId); - - /** - * 视频设备创建 - * - * @param device 设备对象 - * @return true:创建成功 false:创建失败 - */ - public boolean create(Device device); - - /** - * 视频设备更新 - * - * @param device 设备对象 - * @return true:创建成功 false:创建失败 - */ - public boolean updateDevice(Device device); - - /** - * 添加设备通道 - * - * @param deviceId 设备id - * @param channel 通道 - */ - public void updateChannel(String deviceId, DeviceChannel channel); - - /** - * 批量添加设备通道 - * - * @param deviceId 设备id - * @param channels 多个通道 - */ - public void updateChannels(String deviceId, List channels); - - /** - * 开始播放 - * @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, int page, int count); - - /** - * 获取某个设备的通道列表 - * - * @param deviceId 设备ID - * @return - */ - public List queryChannelsByDeviceId(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); - - /** - * 获取多个设备 - * - * @return List 设备对象数组 - */ - public List queryVideoDeviceList(); - - /** - * 删除设备 - * - * @param deviceId 设备ID - * @return true:删除成功 false:删除失败 - */ - public boolean delete(String deviceId); - - /** - * 更新设备在线 - * - * @param deviceId 设备ID - * @return true:更新成功 false:更新失败 - */ - public boolean online(String deviceId); - - /** - * 更新设备离线 - * - * @param deviceId 设备ID - * @return true:更新成功 false:更新失败 - */ - public boolean outline(String deviceId); - - /** - * 更新所有设备离线 - * - * @return true:更新成功 false:更新失败 - */ - public boolean outlineForAll(); - - - /** - * 查询子设备 - * - * @param deviceId - * @param channelId - * @param page - * @param count - * @return - */ - PageInfo querySubChannels(String deviceId, String channelId, String query, Boolean hasSubChannel, String 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); - - - /** - * 分页获取上级平台 - * @param page - * @param count - * @return - */ - PageInfo queryParentPlatformList(int page, int count); - - /** - * 获取所有已启用的平台 - * @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, Boolean inPlatform); - - /** - * 查询设备的通道信息 - */ - List queryChannelListInParentPlatform(String platformId); - - - /** - * 更新上级平台的通道信息 - * @param platformId - * @param channelReduces - * @return - */ - int updateChannelForGB(String platformId, List channelReduces); - - /** - * 移除上级平台的通道信息 - * @param platformId - * @param channelReduces - * @return - */ - int delChannelForGB(String platformId, List channelReduces); - - - DeviceChannel queryChannelInParentPlatform(String platformId, String channelId); - - Device queryVideoDeviceByPlatformIdAndChannelId(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 startTime, String endTime); - - /** - * 查询最新移动位置 - * @param deviceId - */ - public MobilePosition queryLatestPosition(String deviceId); - - /** - * 删除指定设备的所有移动位置 - * @param deviceId - */ - public int clearMobilePositionsByDeviceId(String deviceId); - - /** - * 新增代理流 - * @param streamProxyDto - * @return - */ - public boolean addStreamProxy(StreamProxyItem streamProxyDto); - - /** - * 更新代理流 - * @param streamProxyDto - * @return - */ - public boolean updateStreamProxy(StreamProxyItem streamProxyDto); - - /** - * 移除代理流 - * @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 - */ - List queryStreamInParentPlatform(String platformId, String channelId); - - /** - * 获取平台关联的直播流 - * @param platformId - * @return - */ - List queryGbStreamListInPlatform(String platformId); - - /** - * 批量更新推流列表 - * @param streamPushItems - */ - void updateMediaList(List streamPushItems); - - /** - * 更新单个推流 - * @param streamPushItem - */ - void updateMedia(StreamPushItem streamPushItem); - - /** - * 移除单个推流 - * @param app - * @param stream - */ - int removeMedia(String app, String stream); - - - /** - * 清空推流列表 - */ - void clearMediaList(); - - /** - * 设置流离线 - * @param app - * @param streamId - */ - int mediaOutline(String app, String streamId); - - /** - * 设置平台在线/离线 - * @param online - */ - void updateParentPlatformStatus(String platformGbID, boolean online); - - /** - * 更新媒体节点 - * @param mediaServerItem - */ - void updateMediaServer(MediaServerItem mediaServerItem); - - /** - * 根据媒体ID获取启用/不启用的代理列表 - * @param id 媒体ID - * @param b 启用/不启用 - * @return - */ - List getStreamProxyListForEnableInMediaServer(String id, boolean b); - - /** - * 根据通道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); - -} +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.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, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List channelIds); + + + /** + * 获取某个设备的通道列表 + * + * @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 id); + + int delCatalog(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); +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java index 7e4a544e..f67e152f 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceAlarmMapper.java @@ -15,20 +15,20 @@ import java.util.List; @Repository public interface DeviceAlarmMapper { - @Insert("INSERT INTO device_alarm (deviceId, channelId, alarmPriority, alarmMethod, alarmTime, alarmDescription, longitude, latitude, alarmType ) " + - "VALUES ('${deviceId}', '${channelId}', '${alarmPriority}', '${alarmMethod}', '${alarmTime}', '${alarmDescription}', ${longitude}, ${latitude}, '${alarmType}')") + @Insert("INSERT INTO device_alarm (deviceId, channelId, alarmPriority, alarmMethod, alarmTime, alarmDescription, longitude, latitude, alarmType , createTime ) " + + "VALUES (#{deviceId}, #{channelId}, #{alarmPriority}, #{alarmMethod}, #{alarmTime}, #{alarmDescription}, #{longitude}, #{latitude}, #{alarmType}, #{createTime})") int add(DeviceAlarm alarm); @Select(value = {" "}) List query(String deviceId, String alarmPriority, String alarmMethod, @@ -38,10 +38,10 @@ public interface DeviceAlarmMapper { @Delete(" " ) int clearAlarmBeforeTime(Integer id, List deviceIdList, String time); 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 index 1d0b3659..8da1a09b 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java @@ -1,6 +1,8 @@ package com.genersoft.iot.vmp.storager.dao; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; @@ -16,60 +18,70 @@ public interface DeviceChannelMapper { @Insert("INSERT INTO device_channel (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, createTime, updateTime) " + - "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},'${createTime}', '${updateTime}')") + "ipAddress, port, password, PTZType, status, streamId, longitude, latitude, longitudeGcj02, latitudeGcj02, " + + "longitudeWgs84, latitudeWgs84, hasAudio, createTime, updateTime, businessGroupId, gpsTime) " + + "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})") int add(DeviceChannel channel); @Update(value = {" "}) int update(DeviceChannel channel); @Select(value = {" "}) - List queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online); - - @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId}") - List queryChannelsByDeviceId(String deviceId); + List queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online,List channelIds); @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}") DeviceChannel queryChannel(String deviceId, String channelId); @@ -86,28 +98,44 @@ public interface DeviceChannelMapper { @Update(value = {"UPDATE device_channel SET streamId=#{streamId} WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) void startPlay(String deviceId, String channelId, String streamId); + @Select(value = {" "}) + List queryChannelListInAll(String query, Boolean online, Boolean hasSubChannel, String platformId, String catalogId); @Select(value = {" "}) + List queryChannelByPlatformId(String platformId); - List queryChannelListInAll(String query, Boolean online, Boolean hasSubChannel, String platformId, Boolean inPlatform); @Select("SELECT * FROM device_channel WHERE channelId=#{channelId}") List queryChannelByChannelId( String channelId); @@ -115,57 +143,218 @@ public interface DeviceChannelMapper { @Update(value = {"UPDATE device_channel SET status=0 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) void offline(String deviceId, String channelId); - @Update(value = {"UPDATE device_channel SET status=1 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) - void online(String deviceId, String channelId); + @Update(value = {"UPDATE device_channel SET status=0 WHERE deviceId=#{deviceId}"}) + void offlineByDeviceId(String deviceId); @Insert("") - void batchAdd(List addChannels); + int batchAdd(List addChannels); + + @Update(value = {"UPDATE device_channel SET status=1 WHERE deviceId=#{deviceId} AND channelId=#{channelId}"}) + void online(String deviceId, String channelId); @Update({""}) - void batchUpdate(List updateChannels); + int batchUpdate(List updateChannels); + + + @Select(value = {" "}) + List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String parentChannelId, String query, + Boolean hasSubChannel, Boolean online, int start, int limit,List channelIds); + + @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1") + List queryOnlineChannelsByDeviceId(String deviceId); + + @Delete(value = {" "}) + int cleanChannelsNotInList(String deviceId, List channels); + + @Update(" update device_channel" + + " set subCount = (select *" + + " from (select count(0)" + + " from device_channel" + + " where deviceId = #{deviceId} and parentId = #{channelId}) as temp)" + + " where deviceId = #{deviceId} " + + " and channelId = #{channelId}") + int updateChannelSubCount(String deviceId, String channelId); + + @Update(value = {" "}) + void updatePosition(DeviceChannel deviceChannel); + + @Select("SELECT * FROM device_channel WHERE length(trim(streamId)) > 0") + List getAllChannelInPlay(); + + @Select("select * from device_channel where longitude*latitude > 0 and deviceId = #{deviceId}") + List getAllChannelWithCoordinate(String deviceId); + + + @Select(value = {" "}) + List getChannelsWithCivilCodeAndLength(String deviceId, String parentId, Integer length); + + @Select(value = {" "}) + List getChannelsByCivilCode(String deviceId, String parentId); + + @Select("select min(length(channelId)) as minLength " + + "from device_channel " + + "where deviceId=#{deviceId}") + Integer getChannelMinLength(String deviceId); + + @Select("select * from device_channel where deviceId=#{deviceId} and civilCode not in " + + "(select civilCode from device_channel where deviceId=#{deviceId} group by civilCode)") + List getChannelWithoutCiviCode(String deviceId); + + @Select("select * from device_channel where deviceId=#{deviceId} and SUBSTRING(channelId, 11, 3)=#{typeCode}") + List getBusinessGroups(String deviceId, String typeCode); + + @Select("select dc.id, dc.channelId, dc.deviceId, dc.name, dc.manufacture,dc.model,dc.owner, pc.civilCode,dc.block, " + + " dc.address, '0' as parental,'0' as channelType, pc.id as parentId, dc.safetyWay, dc.registerWay,dc.certNum, dc.certifiable, " + + " dc.errCode,dc.endTime, dc.secrecy, dc.ipAddress, dc.port, dc.PTZType, dc.password, dc.status, " + + " dc.longitudeWgs84 as longitude, dc.latitudeWgs84 as latitude, pc.businessGroupId " + + " from device_channel dc" + + " left join platform_gb_channel pgc on dc.id = pgc.deviceChannelId" + + " left join platform_catalog pc on pgc.catalogId = pc.id and pgc.platformId = pc.platformId" + + " where pgc.platformId=#{serverGBId}") + List queryChannelWithCatalog(String serverGBId); + + @Select("select * from device_channel where deviceId = #{deviceId}") + List queryAllChannels(String deviceId); + + + @Select("select count(1) as total, sum(status) as online from device_channel") + ResourceBaceInfo getOverview(); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java index ed2d8c89..8143d35a 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.storager.dao; import com.genersoft.iot.vmp.gb28181.bean.Device; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; @@ -13,7 +14,35 @@ import java.util.List; @Repository public interface DeviceMapper { - @Select("SELECT * FROM device WHERE deviceId = #{deviceId}") + @Select("SELECT " + + "deviceId, " + + "coalesce(custom_name, name) as name, " + + "password, " + + "manufacturer, " + + "model, " + + "firmware, " + + "transport," + + "streamMode," + + "ip," + + "sdpIp," + + "localIp," + + "port," + + "hostAddress," + + "expires," + + "registerTime," + + "keepaliveTime," + + "createTime," + + "updateTime," + + "charset," + + "subscribeCycleForCatalog," + + "subscribeCycleForMobilePosition," + + "mobilePositionSubmissionInterval," + + "subscribeCycleForAlarm," + + "ssrcCheck," + + "geoCoordSys," + + "treeType," + + "online" + + " FROM device WHERE deviceId = #{deviceId}") Device getDeviceByDeviceId(String deviceId); @Insert("INSERT INTO device (" + @@ -25,15 +54,24 @@ public interface DeviceMapper { "transport," + "streamMode," + "ip," + + "sdpIp," + + "localIp," + "port," + "hostAddress," + "expires," + "registerTime," + "keepaliveTime," + + "keepaliveIntervalTime," + "createTime," + "updateTime," + "charset," + "subscribeCycleForCatalog," + + "subscribeCycleForMobilePosition," + + "mobilePositionSubmissionInterval," + + "subscribeCycleForAlarm," + + "ssrcCheck," + + "geoCoordSys," + + "treeType," + "online" + ") VALUES (" + "#{deviceId}," + @@ -44,47 +82,202 @@ public interface DeviceMapper { "#{transport}," + "#{streamMode}," + "#{ip}," + + "#{sdpIp}," + + "#{localIp}," + "#{port}," + "#{hostAddress}," + "#{expires}," + "#{registerTime}," + "#{keepaliveTime}," + + "#{keepaliveIntervalTime}," + "#{createTime}," + "#{updateTime}," + "#{charset}," + "#{subscribeCycleForCatalog}," + + "#{subscribeCycleForMobilePosition}," + + "#{mobilePositionSubmissionInterval}," + + "#{subscribeCycleForAlarm}," + + "#{ssrcCheck}," + + "#{geoCoordSys}," + + "#{treeType}," + "#{online}" + ")") int add(Device device); @Update(value = {" "}) int update(Device device); - @Select("SELECT *, (SELECT count(0) FROM device_channel WHERE deviceId=de.deviceId) as channelCount FROM device de") - List getDevices(); + @Select( + " " + ) + List getDevices(Boolean online); @Delete("DELETE FROM device WHERE deviceId=#{deviceId}") int del(String deviceId); @Update("UPDATE device SET online=0") int outlineForAll(); + + @Select("SELECT " + + "deviceId, " + + "coalesce(custom_name, name) as name, " + + "password, " + + "manufacturer, " + + "model, " + + "firmware, " + + "transport," + + "streamMode," + + "ip," + + "sdpIp," + + "localIp," + + "port," + + "hostAddress," + + "expires," + + "registerTime," + + "keepaliveTime," + + "createTime," + + "updateTime," + + "charset," + + "subscribeCycleForCatalog," + + "subscribeCycleForMobilePosition," + + "mobilePositionSubmissionInterval," + + "subscribeCycleForAlarm," + + "ssrcCheck," + + "geoCoordSys," + + "treeType," + + "online " + + " FROM device WHERE online = 1") + List getOnlineDevices(); + @Select("SELECT " + + "deviceId, " + + "coalesce(custom_name, name) as name, " + + "password, " + + "manufacturer, " + + "model, " + + "firmware, " + + "transport," + + "streamMode," + + "ip," + + "sdpIp," + + "localIp," + + "port," + + "hostAddress," + + "expires," + + "registerTime," + + "keepaliveTime," + + "createTime," + + "updateTime," + + "charset," + + "subscribeCycleForCatalog," + + "subscribeCycleForMobilePosition," + + "mobilePositionSubmissionInterval," + + "subscribeCycleForAlarm," + + "ssrcCheck," + + "geoCoordSys," + + "treeType," + + "online" + + " FROM device WHERE ip = #{host} AND port=#{port}") + Device getDeviceByHostAndPort(String host, int port); + + @Update(value = {" "}) + int updateCustom(Device device); + + @Insert("INSERT INTO device (" + + "deviceId, " + + "custom_name, " + + "password, " + + "sdpIp, " + + "createTime," + + "updateTime," + + "charset," + + "ssrcCheck," + + "geoCoordSys," + + "treeType," + + "online" + + ") VALUES (" + + "#{deviceId}," + + "#{name}," + + "#{password}," + + "#{sdpIp}," + + "#{createTime}," + + "#{updateTime}," + + "#{charset}," + + "#{ssrcCheck}," + + "#{geoCoordSys}," + + "#{treeType}," + + "#{online}" + + ")") + void addCustomDevice(Device device); + + @Select("select count(1) as total, sum(online) as online from device") + ResourceBaceInfo getOverview(); + } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java index f3e42615..358836c4 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMobilePositionMapper.java @@ -4,24 +4,23 @@ import java.util.List; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; import org.apache.ibatis.annotations.*; -//import org.springframework.stereotype.Repository; @Mapper -//@Repository public interface DeviceMobilePositionMapper { - @Insert("INSERT INTO device_mobile_position (deviceId,channelId, deviceName, time, longitude, latitude, altitude, speed, direction, reportSource, geodeticSystem, cnLng, cnLat) " + - "VALUES ('${deviceId}','${channelId}', '${deviceName}', '${time}', ${longitude}, ${latitude}, ${altitude}, ${speed}, ${direction}, '${reportSource}', '${geodeticSystem}', '${cnLng}', '${cnLat}')") + @Insert("INSERT INTO device_mobile_position (deviceId,channelId, deviceName, time, longitude, latitude, altitude, speed, direction, reportSource, longitudeGcj02, latitudeGcj02, longitudeWgs84, latitudeWgs84, createTime) " + + "VALUES (#{deviceId},#{channelId}, #{deviceName}, #{time}, #{longitude}, #{latitude}, #{altitude}, #{speed}, #{direction}, #{reportSource}, #{longitudeGcj02}, #{latitudeGcj02}, #{longitudeWgs84}, #{latitudeWgs84}, #{createTime})") int insertNewPosition(MobilePosition mobilePosition); @Select(value = {" "}) - List queryPositionByDeviceIdAndTime(String deviceId, String startTime, String endTime); + List queryPositionByDeviceIdAndTime(String deviceId, String channelId, String startTime, String endTime); @Select("SELECT * FROM device_mobile_position WHERE deviceId = #{deviceId}" + " ORDER BY time DESC LIMIT 1") 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 index ffbca9c9..49101bdd 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/GbStreamMapper.java @@ -1,8 +1,10 @@ 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.springframework.stereotype.Repository; @@ -12,11 +14,12 @@ import java.util.List; @Repository public interface GbStreamMapper { - @Insert("INSERT INTO gb_stream (app, stream, gbId, name, " + - "longitude, latitude, streamType, mediaServerId, status) VALUES" + - "('${app}', '${stream}', '${gbId}', '${name}', " + - "'${longitude}', '${latitude}', '${streamType}', " + - "'${mediaServerId}', ${status})") + @Insert("REPLACE INTO gb_stream (app, stream, gbId, name, " + + "longitude, latitude, streamType, mediaServerId, createTime) VALUES" + + "(#{app}, #{stream}, #{gbId}, #{name}, " + + "#{longitude}, #{latitude}, #{streamType}, " + + "#{mediaServerId}, #{createTime})") + @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gbStreamId") int add(GbStream gbStream); @Update("UPDATE gb_stream " + @@ -27,48 +30,72 @@ public interface GbStreamMapper { "streamType=#{streamType}," + "longitude=#{longitude}, " + "latitude=#{latitude}," + - "mediaServerId=#{mediaServerId}," + - "status=${status} " + + "mediaServerId=#{mediaServerId}" + "WHERE app=#{app} AND stream=#{stream}") + int updateByAppAndStream(GbStream gbStream); + + @Update("UPDATE gb_stream " + + "SET app=#{app}," + + "stream=#{stream}," + + "gbId=#{gbId}," + + "name=#{name}," + + "streamType=#{streamType}," + + "longitude=#{longitude}, " + + "latitude=#{latitude}," + + "mediaServerId=#{mediaServerId}" + + "WHERE gbStreamId=#{gbStreamId}") int update(GbStream gbStream); @Delete("DELETE FROM gb_stream WHERE app=#{app} AND stream=#{stream}") int del(String app, String stream); - @Select("SELECT gs.*, pgs.platformId FROM gb_stream gs LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream") - List selectAll(); + @Select("") + List selectAll(String platformId, String catalogId, String query, String mediaServerId); @Select("SELECT * FROM gb_stream WHERE app=#{app} AND stream=#{stream}") - StreamProxyItem selectOne(String app, String stream); + GbStream selectOne(String app, String stream); @Select("SELECT * FROM gb_stream WHERE gbId=#{gbId}") List selectByGBId(String gbId); - @Select("SELECT gs.*, pgs.platformId FROM gb_stream gs " + - "LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream " + - "WHERE gs.gbId = '${gbId}' AND pgs.platformId = '${platformId}'") - List queryStreamInPlatform(String platformId, String gbId); + @Select("SELECT gs.*, pgs.platformId as platformId, pgs.catalogId as catalogId FROM gb_stream gs " + + "LEFT JOIN platform_gb_stream pgs ON gs.gbStreamId = pgs.gbStreamId " + + "WHERE gs.gbId = #{gbId} AND pgs.platformId = #{platformId}") + GbStream queryStreamInPlatform(String platformId, String gbId); - @Select("SELECT gs.*, pgs.platformId FROM gb_stream gs " + - "LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream " + - "WHERE pgs.platformId = '${platformId}'") - List queryGbStreamListInPlatform(String platformId); + @Select("") + List queryGbStreamListInPlatform(String platformId, boolean usPushingAsStatus); - @Update("UPDATE gb_stream " + - "SET status=${status} " + - "WHERE app=#{app} AND stream=#{stream}") - int setStatus(String app, String stream, boolean status); - @Select("SELECT gs.*, pgs.platformId FROM gb_stream gs LEFT JOIN platform_gb_stream pgs ON gs.app = pgs.app AND gs.stream = pgs.stream WHERE mediaServerId=#{mediaServerId} ") - List selectAllByMediaServerId(String mediaServerId); - - @Update("UPDATE gb_stream " + - "SET status=${status} " + - "WHERE mediaServerId=#{mediaServerId} ") - void updateStatusByMediaServerId(String mediaServerId, boolean status); - - @Select("SELECT * FROM gb_stream WHERE mediaServerId=#{mediaServerId}") - void delByMediaServerId(String mediaServerId); + @Select("SELECT gs.* FROM gb_stream gs LEFT JOIN platform_gb_stream pgs " + + "ON gs.gbStreamId = pgs.gbStreamId WHERE pgs.gbStreamId is NULL") + List queryStreamNotInPlatform(); @Delete("DELETE FROM gb_stream WHERE streamType=#{type} AND gbId=NULL AND mediaServerId=#{mediaServerId}") void deleteWithoutGBId(String type, String mediaServerId); @@ -81,16 +108,65 @@ public interface GbStreamMapper { "") void batchDel(List streamProxyItemList); + @Delete("") + void batchDelForGbStream(List gbStreams); + @Insert("") + @Options(useGeneratedKeys = true, keyProperty = "gbStreamId", keyColumn = "gbStreamId") void batchAdd(List subList); + + @Update({""}) + int updateStreamGPS(List gpsMsgInfos); + + @Select("") + List selectAllForAppAndStream(List streamPushItems); + + @Update("UPDATE gb_stream " + + "SET mediaServerId=#{mediaServerId}" + + "WHERE app=#{app} AND stream=#{stream}") + void updateMediaServer(String app, String stream, String mediaServerId); + + @Update("") + int updateGbIdOrName(List streamPushItemForUpdate); + + @Select("SELECT status FROM stream_proxy WHERE app=#{app} AND stream=#{stream}") + Boolean selectStatusForProxy(String app, String stream); + + @Select("SELECT status FROM stream_push WHERE app=#{app} AND stream=#{stream}") + Boolean selectStatusForPush(String app, String stream); + } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java index 18fa91b6..fb1b4e30 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java @@ -18,22 +18,20 @@ import java.util.List; public interface LogMapper { @Insert("insert into log ( name, type, uri, address, result, timing, username, createTime) " + - "values ('${name}', '${type}', '${uri}', '${address}', '${result}', ${timing}, '${username}', '${createTime}')") + "values (#{name}, #{type}, #{uri}, #{address}, #{result}, #{timing}, #{username}, #{createTime})") int add(LogDto logDto); - @Select(value = {""}) List query(String query, String type, String startTime, String endTime); - @Delete("DELETE FROM log") int clear(); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java index 4e09e797..97e74ae8 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/MediaServerMapper.java @@ -1,10 +1,7 @@ package com.genersoft.iot.vmp.storager.dao; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; -import org.apache.ibatis.annotations.Insert; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Select; -import org.apache.ibatis.annotations.Update; +import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; import java.util.List; @@ -29,10 +26,8 @@ public interface MediaServerMapper { "rtspSSLPort, " + "autoConfig, " + "secret, " + - "streamNoneReaderDelayMS, " + "rtpEnable, " + "rtpPortRange, " + - "sendRtpPortRange, " + "recordAssistPort, " + "defaultServer, " + "createTime, " + @@ -40,98 +35,92 @@ public interface MediaServerMapper { "hookAliveInterval" + ") VALUES " + "(" + - "'${id}', " + - "'${ip}', " + - "'${hookIp}', " + - "'${sdpIp}', " + - "'${streamIp}', " + - "${httpPort}, " + - "${httpSSlPort}, " + - "${rtmpPort}, " + - "${rtmpSSlPort}, " + - "${rtpProxyPort}, " + - "${rtspPort}, " + - "${rtspSSLPort}, " + - "${autoConfig}, " + - "'${secret}', " + - "${streamNoneReaderDelayMS}, " + - "${rtpEnable}, " + - "'${rtpPortRange}', " + - "'${sendRtpPortRange}', " + - "${recordAssistPort}, " + - "${defaultServer}, " + - "'${createTime}', " + - "'${updateTime}', " + - "${hookAliveInterval})") + "#{id}, " + + "#{ip}, " + + "#{hookIp}, " + + "#{sdpIp}, " + + "#{streamIp}, " + + "#{httpPort}, " + + "#{httpSSlPort}, " + + "#{rtmpPort}, " + + "#{rtmpSSlPort}, " + + "#{rtpProxyPort}, " + + "#{rtspPort}, " + + "#{rtspSSLPort}, " + + "#{autoConfig}, " + + "#{secret}, " + + "#{rtpEnable}, " + + "#{rtpPortRange}, " + + "#{recordAssistPort}, " + + "#{defaultServer}, " + + "#{createTime}, " + + "#{updateTime}, " + + "#{hookAliveInterval})") int add(MediaServerItem mediaServerItem); @Update(value = {" "}) int update(MediaServerItem mediaServerItem); @Update(value = {" "}) int updateByHostAndPort(MediaServerItem mediaServerItem); - @Select("SELECT * FROM media_server WHERE id='${id}'") + @Select("SELECT * FROM media_server WHERE id=#{id}") MediaServerItem queryOne(String id); @Select("SELECT * FROM media_server") List queryAll(); - @Select("DELETE FROM media_server WHERE id='${id}'") + @Delete("DELETE FROM media_server WHERE id=#{id}") void delOne(String id); - @Select("DELETE FROM media_server WHERE ip='${host}' and httpPort=${port}") + @Select("DELETE FROM media_server WHERE ip=#{host} and httpPort=#{port}") void delOneByIPAndPort(String host, int port); - @Select("DELETE FROM media_server WHERE defaultServer=1;") - void delDefault(); + @Delete("DELETE FROM media_server WHERE defaultServer=1") + int delDefault(); - @Select("SELECT * FROM media_server WHERE ip='${host}' and httpPort=${port}") + @Select("SELECT * FROM media_server WHERE ip=#{host} and httpPort=#{port}") MediaServerItem queryOneByHostAndPort(String host, int port); @Select("SELECT * FROM media_server WHERE defaultServer=1") 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 index c92711ae..52025eb5 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java @@ -1,6 +1,7 @@ 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.springframework.stereotype.Repository; @@ -15,10 +16,10 @@ public interface ParentPlatformMapper { @Insert("INSERT INTO parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp, " + " devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, " + - " status, shareAllLiveStream) " + - " VALUES (${enable}, '${name}', '${serverGBId}', '${serverGBDomain}', '${serverIP}', ${serverPort}, '${deviceGBId}', '${deviceIp}', " + - " '${devicePort}', '${username}', '${password}', '${expires}', '${keepTimeout}', '${transport}', '${characterSet}', ${ptz}, ${rtcp}, " + - " ${status}, ${shareAllLiveStream})") + " status, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime, treeType) " + + " VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " + + " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, " + + " #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{treeType})") int addParentPlatform(ParentPlatform parentPlatform); @Update("UPDATE parent_platform " + @@ -40,7 +41,13 @@ public interface ParentPlatformMapper { "ptz=#{ptz}, " + "rtcp=#{rtcp}, " + "status=#{status}, " + - "shareAllLiveStream=#{shareAllLiveStream} " + + "startOfflinePush=#{startOfflinePush}, " + + "catalogGroup=#{catalogGroup}, " + + "administrativeDivision=#{administrativeDivision}, " + + "createTime=#{createTime}, " + + "updateTime=#{updateTime}, " + + "treeType=#{treeType}, " + + "catalogId=#{catalogId} " + "WHERE id=#{id}") int updateParentPlatform(ParentPlatform parentPlatform); @@ -53,7 +60,11 @@ public interface ParentPlatformMapper { " +\n" + " (SELECT count(0)\n" + " FROM platform_gb_stream pgs\n" + - " WHERE pgs.platformId = pp.serverGBId)) as channelCount\n" + + " WHERE pgs.platformId = pp.serverGBId)\n" + + " +\n" + + " (SELECT count(0)\n" + + " FROM platform_catalog pgc\n" + + " WHERE pgc.platformId = pp.serverGBId)) as channelCount\n" + "FROM parent_platform pp ") List getParentPlatformList(); @@ -72,6 +83,15 @@ public interface ParentPlatformMapper { @Update("UPDATE parent_platform SET status=#{online} WHERE serverGBId=#{platformGbID}" ) int updateParentPlatformStatus(String platformGbID, boolean online); - @Select("SELECT * FROM parent_platform WHERE shareAllLiveStream=true") - List selectAllAhareAllLiveStream(); + @Update(value = {" "}) + int setDefaultCatalog(String platformId, String catalogId, String updateTime); + + @Select("select 'channel' as name, count(pgc.platformId) count from platform_gb_channel pgc left join device_channel dc on dc.id = pgc.deviceChannelId where pgc.platformId=#{platformId} and dc.channelId =#{gbId} " + + "union " + + "select 'stream' as name, count(pgs.platformId) count from platform_gb_stream pgs left join gb_stream gs on pgs.gbStreamId = gs.gbStreamId where pgs.platformId=#{platformId} and gs.gbId = #{gbId}") + List getChannelSource(String platformId, 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 new file mode 100644 index 00000000..f0baf9e9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformCatalogMapper.java @@ -0,0 +1,56 @@ +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.gb28181.bean.PlatformCatalog; +import com.genersoft.iot.vmp.gb28181.bean.PlatformGbStream; +import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Mapper +@Repository +public interface PlatformCatalogMapper { + + @Insert("INSERT INTO platform_catalog (id, name, platformId, parentId, civilCode, businessGroupId) VALUES" + + "(#{id}, #{name}, #{platformId}, #{parentId}, #{civilCode}, #{businessGroupId})") + int add(PlatformCatalog platformCatalog); + + @Delete("DELETE FROM platform_catalog WHERE id=#{id}") + int del(String id); + + @Delete("DELETE FROM platform_catalog WHERE platformId=#{platformId}") + int delByPlatformId(String platformId); + + @Select("SELECT pc.*, count(pc2.id) as childrenCount FROM platform_catalog pc " + + "left join platform_catalog pc2 on pc.id = pc2.parentId " + + "WHERE pc.parentId=#{parentId} AND pc.platformId=#{platformId} group by pc.id") + List selectByParentId(String platformId, String parentId); + + @Select("SELECT *, (SELECT COUNT(1) from platform_catalog where parentId = pc.id) as childrenCount FROM platform_catalog pc WHERE pc.id=#{id}") + PlatformCatalog select(String id); + + @Update(value = {" "}) + int update(PlatformCatalog platformCatalog); + + @Select("SELECT *, (SELECT COUNT(1) from platform_catalog where parentId = pc.id) as childrenCount FROM platform_catalog pc WHERE pc.platformId=#{platformId}") + List selectByPlatForm(String platformId); + + @Select("SELECT pc.* FROM platform_catalog pc WHERE pc.id = (SELECT pp.catalogId from parent_platform pp WHERE pp.serverGBId=#{platformId})") + PlatformCatalog selectDefaultByPlatFormId(String platformId); + + + @Select("SELECT pc.* FROM platform_catalog pc WHERE pc.id = #{id}") + PlatformCatalog selectParentCatalog(String id); + + @Select("SELECT pc.id as channelId, pc.name, pc.civilCode, pc.businessGroupId,'1' as parental, pc.parentId " + + " FROM platform_catalog pc WHERE pc.platformId=#{platformId}") + List queryCatalogInPlatform(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 index c8130c34..35a42c57 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformChannelMapper.java @@ -2,6 +2,8 @@ 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; @@ -19,41 +21,97 @@ public interface PlatformChannelMapper { * 查询列表里已经关联的 */ @Select("") - List findChannelRelatedPlatform(String platformId, List deviceAndChannelIds); + List findChannelRelatedPlatform(String platformId, List channelReduces); @Insert("") int addChannels(String platformId, List channelReducesToAdd); - @Delete("") int delChannelForGB(String platformId, List channelReducesToDel); @Delete("") int delChannelForDeviceId(String deviceId); @Delete("") int cleanChannelForGB(String platformId); + @Select("SELECT dc.* FROM platform_gb_channel pgc left join device_channel dc on dc.id = pgc.deviceChannelId WHERE dc.channelId=#{channelId} and pgc.platformId=#{platformId}") + List queryChannelInParentPlatform(String platformId, String channelId); - @Select("SELECT * FROM device_channel WHERE deviceId = (SELECT deviceId FROM platform_gb_channel WHERE " + - "platformId='${platformId}' AND channelId='${channelId}' ) AND channelId='${channelId}'") - DeviceChannel queryChannelInParentPlatform(String platformId, String channelId); + @Select("SELECT dc.* FROM platform_gb_channel pgc left join device_channel dc on dc.id = pgc.deviceChannelId WHERE pgc.platformId=#{platformId} and pgc.catalogId=#{catalogId}") + List queryAllChannelInCatalog(String platformId, String catalogId); - @Select("SELECT * FROM device WHERE deviceId = (SELECT deviceId FROM platform_gb_channel WHERE platformId='${platformId}' AND channelId='${channelId}')") - Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); + @Select(" select dc.channelId as id, dc.name as name, pgc.platformId as platformId, pgc.catalogId as parentId, 0 as childrenCount, 1 as type " + + " from device_channel dc left join platform_gb_channel pgc on dc.id = pgc.deviceChannelId " + + " where pgc.platformId=#{platformId} and pgc.catalogId=#{catalogId}") + List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId); + + @Select("select d.*\n" + + "from platform_gb_channel pgc\n" + + " left join device_channel dc on dc.id = pgc.deviceChannelId\n" + + " left join device d on dc.deviceId = d.deviceId\n" + + "where dc.channelId = #{channelId} and pgc.platformId=#{platformId}") + List queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId); + + @Delete("") + int delByCatalogId(String id); + + @Delete("") + int delByCatalogIdAndChannelIdAndPlatformId(PlatformCatalog platformCatalog); + + @Select(" ") + List queryPlatFormListForGBWithGBId(String channelId, List platforms); + + @Delete("") + void delByPlatformId(String serverGBId); + + @Delete("") + int delChannelForGBByCatalogId(String platformId, String catalogId); + + @Select("select dc.channelId deviceId,dc.name,d.manufacturer,d.model,d.firmware\n" + + "from platform_gb_channel pgc\n" + + " left join device_channel dc on dc.id = pgc.deviceChannelId\n" + + " left join device d on dc.deviceId = d.deviceId\n" + + "where dc.channelId = #{channelId} and pgc.platformId=#{platformId}") + List queryDeviceInfoByPlatformIdAndChannelId(String platformId, 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 index a51eda2d..91a4a5f4 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/PlatformGbStreamMapper.java @@ -1,7 +1,11 @@ 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; @@ -12,19 +16,96 @@ import java.util.List; @Repository public interface PlatformGbStreamMapper { - @Insert("INSERT INTO platform_gb_stream (app, stream, platformId) VALUES" + - "('${app}', '${stream}', '${platformId}')") + @Insert("REPLACE INTO platform_gb_stream (gbStreamId, platformId, catalogId) VALUES" + + "( #{gbStreamId}, #{platformId}, #{catalogId})") int add(PlatformGbStream platformGbStream); - @Delete("DELETE FROM platform_gb_stream WHERE app=#{app} AND stream=#{stream}") + + @Insert("") + int batchAdd(List streamPushItems); + + @Delete("DELETE FROM platform_gb_stream WHERE gbStreamId = (select gbStreamId from gb_stream where app=#{app} AND stream=#{stream})") int delByAppAndStream(String app, String stream); @Delete("DELETE FROM platform_gb_stream WHERE platformId=#{platformId}") int delByPlatformId(String platformId); - @Select("SELECT * FROM platform_gb_stream WHERE app=#{app} AND stream=#{stream}") - List selectByAppAndStream(String app, String stream); + @Select("SELECT " + + "pp.* " + + "FROM " + + "platform_gb_stream pgs " + + "LEFT JOIN parent_platform pp ON pp.serverGBId = pgs.platformId " + + "LEFT JOIN gb_stream gs ON gs.gbStreamId = pgs.gbStreamId " + + "WHERE " + + "gs.app =#{app} " + + "AND gs.stream =#{stream} ") + List selectByAppAndStream(String app, String stream); - @Select("SELECT * FROM platform_gb_stream WHERE app=#{app} AND stream=#{stream} AND platformId=#{serverGBId}") + @Select("SELECT pgs.*, gs.gbId FROM platform_gb_stream pgs " + + "LEFT JOIN gb_stream gs ON pgs.gbStreamId = gs.gbStreamId " + + "WHERE gs.app=#{app} AND gs.stream=#{stream} AND pgs.platformId=#{serverGBId}") StreamProxyItem selectOne(String app, String stream, String serverGBId); + + @Select("select gs.* \n" + + "from gb_stream gs\n" + + " left join platform_gb_stream pgs\n" + + " on gs.gbStreamId = pgs.gbStreamId\n" + + "where pgs.platformId=#{platformId} and pgs.catalogId=#{catalogId}") + List queryChannelInParentPlatformAndCatalog(String platformId, String catalogId); + + @Select("select gs.gbId as id, gs.name as name, pgs.platformId as platformId, pgs.catalogId as catalogId , 0 as childrenCount, 2 as type\n" + + "from gb_stream gs\n" + + " left join platform_gb_stream pgs\n" + + " on gs.gbStreamId = pgs.gbStreamId\n" + + "where pgs.platformId=#{platformId} and pgs.catalogId=#{catalogId}") + List queryChannelInParentPlatformAndCatalogForCatalog(String platformId, String catalogId); + + @Delete("DELETE FROM platform_gb_stream WHERE catalogId=#{id}") + int delByCatalogId(String id); + + @Select(" ") + List queryPlatFormListForGBWithGBId(String app, String stream, List platforms); + + @Delete("DELETE FROM platform_gb_stream WHERE gbStreamId = (select id from gb_stream where app=#{app} AND stream=#{stream}) AND platformId=#{platformId}") + int delByAppAndStreamAndPlatform(String app, String stream, String platformId); + + @Delete("") + void delByGbStreams(List gbStreams); + + @Delete("") + void delByAppAndStreamsByPlatformId(List gbStreams, String platformId); + + @Delete("DELETE FROM platform_gb_stream WHERE platformId=#{platformId} and catalogId=#{catalogId}") + int delByPlatformAndCatalogId(String platformId, String catalogId); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordInfoDao.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordInfoDao.java index 2d73982c..a784472f 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordInfoDao.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/RecordInfoDao.java @@ -14,10 +14,10 @@ import java.util.List; public interface RecordInfoDao { @Insert("INSERT INTO recordInfo (app, stream, mediaServerId, createTime, type, deviceId, channelId, name) VALUES" + - "('${app}', '${stream}', '${mediaServerId}', datetime('now','localtime')), '${type}', '${deviceId}', '${channelId}', '${name}'") + "(#{app}, #{stream}, #{mediaServerId}, datetime('now','localtime')), #{type}, #{deviceId}, #{channelId}, #{name}") int add(RecordInfo recordInfo); - @Delete("DELETE FROM user WHERE createTime < '${beforeTime}'") + @Delete("DELETE FROM user WHERE createTime < #{beforeTime}") int deleteBefore(String beforeTime); @Select("select * FROM recordInfo") diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/RoleMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/RoleMapper.java index be20a695..425f5e42 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/RoleMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/RoleMapper.java @@ -11,25 +11,25 @@ import java.util.List; @Repository public interface RoleMapper { - @Insert("INSERT INTO role (name, authority, createTime, updateTime) VALUES" + - "('${name}', '${authority}', '${createTime}', '${updateTime}')") + @Insert("INSERT INTO user_role (name, authority, createTime, updateTime) VALUES" + + "(#{name}, #{authority}, #{createTime}, #{updateTime})") int add(Role role); @Update(value = {" "}) int update(Role role); - @Delete("DELETE FROM role WHERE id != 1 and id=#{id}") + @Delete("DELETE FROM user_role WHERE id != 1 and id=#{id}") int delete(int id); - @Select("select * FROM role WHERE id=#{id}") + @Select("select * FROM user_role WHERE id=#{id}") Role selectById(int id); - @Select("select * FROM role") + @Select("select * FROM user_role") List selectAll(); } 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 index b6e1ba1a..5dbd8f04 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamProxyMapper.java @@ -1,6 +1,7 @@ package com.genersoft.iot.vmp.storager.dao; import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; @@ -10,15 +11,16 @@ import java.util.List; @Repository public interface StreamProxyMapper { - @Insert("INSERT INTO stream_proxy (type, app, stream,mediaServerId, url, src_url, dst_url, " + - "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_hls, enable_mp4, enable, enable_remove_none_reader, createTime) VALUES" + - "('${type}','${app}', '${stream}', '${mediaServerId}','${url}', '${src_url}', '${dst_url}', " + - "'${timeout_ms}', '${ffmpeg_cmd_key}', '${rtp_type}', ${enable_hls}, ${enable_mp4}, ${enable}, " + - "${enable_remove_none_reader}, '${createTime}' )") + @Insert("INSERT INTO stream_proxy (type, name, app, stream,mediaServerId, url, src_url, dst_url, " + + "timeout_ms, ffmpeg_cmd_key, rtp_type, enable_audio, enable_mp4, enable, status, enable_remove_none_reader, enable_disable_none_reader, createTime) VALUES" + + "(#{type}, #{name}, #{app}, #{stream}, #{mediaServerId}, #{url}, #{src_url}, #{dst_url}, " + + "#{timeout_ms}, #{ffmpeg_cmd_key}, #{rtp_type}, #{enable_audio}, #{enable_mp4}, #{enable}, #{status}, " + + "#{enable_remove_none_reader}, #{enable_disable_none_reader}, #{createTime} )") int add(StreamProxyItem streamProxyDto); @Update("UPDATE stream_proxy " + "SET type=#{type}, " + + "name=#{name}," + "app=#{app}," + "stream=#{stream}," + "url=#{url}, " + @@ -28,9 +30,11 @@ public interface StreamProxyMapper { "timeout_ms=#{timeout_ms}, " + "ffmpeg_cmd_key=#{ffmpeg_cmd_key}, " + "rtp_type=#{rtp_type}, " + - "enable_hls=#{enable_hls}, " + + "enable_audio=#{enable_audio}, " + "enable=#{enable}, " + + "status=#{status}, " + "enable_remove_none_reader=#{enable_remove_none_reader}, " + + "enable_disable_none_reader=#{enable_disable_none_reader}, " + "enable_mp4=#{enable_mp4} " + "WHERE app=#{app} AND stream=#{stream}") int update(StreamProxyItem streamProxyDto); @@ -41,7 +45,7 @@ public interface StreamProxyMapper { @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream order by st.createTime desc") List selectAll(); - @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=${enable} order by st.createTime desc") + @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=#{enable} order by st.createTime desc") List selectForEnable(boolean enable); @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream} order by st.createTime desc") @@ -49,22 +53,30 @@ public interface StreamProxyMapper { @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st " + "LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " + - "WHERE st.enable=${enable} and st.mediaServerId = '${id}' order by st.createTime desc") + "WHERE st.enable=#{enable} and st.mediaServerId = #{id} order by st.createTime desc") List selectForEnableInMediaServer(String id, boolean enable); @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st " + "LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream " + - "WHERE st.mediaServerId = '${id}' order by st.createTime desc") + "WHERE st.mediaServerId = #{id} order by st.createTime desc") List selectInMediaServer(String id); @Update("UPDATE stream_proxy " + - "SET enable=#{status} " + + "SET status=#{status} " + "WHERE mediaServerId=#{mediaServerId}") - void updateStatus(boolean status, String mediaServerId); + void updateStatusByMediaServerId(String mediaServerId, boolean status); + + @Update("UPDATE stream_proxy " + + "SET status=#{status} " + + "WHERE app=#{app} AND stream=#{stream}") + int updateStatus(String app, String stream, boolean status); @Delete("DELETE FROM stream_proxy WHERE enable_remove_none_reader=true AND mediaServerId=#{mediaServerId}") void deleteAutoRemoveItemByMediaServerId(String mediaServerId); @Select("SELECT st.*, pgs.gbId, pgs.name, pgs.longitude, pgs.latitude FROM stream_proxy st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable_remove_none_reader=true AND st.mediaServerId=#{mediaServerId} order by st.createTime desc") List selecAutoRemoveItemByMediaServerId(String mediaServerId); + + @Select("select count(1) as total, sum(status) as online from stream_proxy") + ResourceBaceInfo getOverview(); } 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 index c5b22f8b..492dfe38 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/StreamPushMapper.java @@ -1,7 +1,11 @@ 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 com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; import org.apache.ibatis.annotations.*; +// import org.omg.PortableInterceptor.INACTIVE; import org.springframework.stereotype.Repository; import java.util.Collection; @@ -12,26 +16,39 @@ import java.util.List; public interface StreamPushMapper { @Insert("INSERT INTO stream_push (app, stream, totalReaderCount, originType, originTypeStr, " + - "createStamp, aliveSecond, mediaServerId) VALUES" + - "('${app}', '${stream}', '${totalReaderCount}', '${originType}', '${originTypeStr}', " + - "'${createStamp}', '${aliveSecond}', '${mediaServerId}' )") + "pushTime, aliveSecond, mediaServerId, serverId, updateTime, createTime, pushIng, self) VALUES" + + "(#{app}, #{stream}, #{totalReaderCount}, #{originType}, #{originTypeStr}, " + + "#{pushTime}, #{aliveSecond}, #{mediaServerId} , #{serverId} , #{updateTime} , #{createTime}, " + + "#{pushIng}, #{self} )") int add(StreamPushItem streamPushItem); - @Update("UPDATE stream_push " + - "SET app=#{app}," + - "stream=#{stream}," + - "mediaServerId=#{mediaServerId}," + - "totalReaderCount=#{totalReaderCount}, " + - "originType=#{originType}," + - "originTypeStr=#{originTypeStr}, " + - "createStamp=#{createStamp}, " + - "aliveSecond=#{aliveSecond} " + - "WHERE app=#{app} AND stream=#{stream}") + + @Update(value = {" "}) int update(StreamPushItem streamPushItem); @Delete("DELETE FROM stream_push WHERE app=#{app} AND stream=#{stream}") int del(String app, String stream); + @Delete("") + int delAllWithoutGBId(List streamPushItems); + @Delete("") int delAll(List streamPushItems); - @Select("SELECT st.*, pgs.gbId, pgs.status, pgs.name, pgs.longitude, pgs.latitude FROM stream_push st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream") + @Delete("") + int delAllForGbStream(List gbStreams); + + + @Select(value = {" "}) + List selectAllForList(String query, Boolean pushing, String mediaServerId); + + @Select("SELECT st.*, gs.gbId, gs.name, gs.longitude, gs.latitude FROM stream_push st LEFT JOIN gb_stream gs on st.app = gs.app AND st.stream = gs.stream order by st.createTime desc") List selectAll(); - @Select("SELECT st.*, pgs.gbId, pgs.status, pgs.name, pgs.longitude, pgs.latitude FROM stream_push st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.enable=${enable}") - List selectForEnable(boolean enable); - - @Select("SELECT st.*, pgs.gbId, pgs.status, pgs.name, pgs.longitude, pgs.latitude FROM stream_push st LEFT JOIN gb_stream pgs on st.app = pgs.app AND st.stream = pgs.stream WHERE st.app=#{app} AND st.stream=#{stream}") + @Select("SELECT st.*, gs.gbId, gs.name, gs.longitude, gs.latitude FROM stream_push st LEFT JOIN gb_stream gs on st.app = gs.app AND st.stream = gs.stream WHERE st.app=#{app} AND st.stream=#{stream}") StreamPushItem selectOne(String app, String stream); @Insert("") - void addAll(List streamPushItems); + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int addAll(List streamPushItems); @Delete("DELETE FROM stream_push") void clear(); - @Delete("DELETE FROM stream_push WHERE mediaServerId=#{mediaServerId}") + @Delete("DELETE sp FROM stream_push sp left join gb_stream gs on gs.app = sp.app and gs.stream= sp.stream WHERE sp.mediaServerId=#{mediaServerId} and gs.gbId is null ") void deleteWithoutGBId(String mediaServerId); @Select("SELECT * FROM stream_push WHERE mediaServerId=#{mediaServerId}") List selectAllByMediaServerId(String mediaServerId); + @Select("SELECT sp.* FROM stream_push sp left join gb_stream gs on gs.app = sp.app and gs.stream= sp.stream WHERE sp.mediaServerId=#{mediaServerId} and gs.gbId is null") + List selectAllByMediaServerIdWithOutGbID(String mediaServerId); + + @Update("UPDATE stream_push " + + "SET status=#{status} " + + "WHERE app=#{app} AND stream=#{stream}") + int updateStatus(String app, String stream, boolean status); + + @Update("UPDATE stream_push " + + "SET pushIng=#{pushIng} " + + "WHERE app=#{app} AND stream=#{stream}") + int updatePushStatus(String app, String stream, boolean pushIng); + + @Update("UPDATE stream_push " + + "SET status=#{status} " + + "WHERE mediaServerId=#{mediaServerId}") + void updateStatusByMediaServerId(String mediaServerId, 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 stream_push sp left join gb_stream gs on sp.app = gs.app AND sp.stream = gs.stream where sp.status = 1") + List getOnlinePusherForGb(); + + @Update("UPDATE stream_push SET status=0") + void setAllStreamOffline(); + + @Select("SELECT CONCAT(app,stream) FROM gb_stream") + List getAllAppAndStream(); + + @Select(value = {" "}) + ResourceBaceInfo getOverview(boolean pushIngAsOnline); } 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 7eb71e6a..ecaa165a 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/UserMapper.java @@ -10,16 +10,17 @@ import java.util.List; @Repository public interface UserMapper { - @Insert("INSERT INTO user (username, password, roleId, createTime, updateTime) VALUES" + - "('${username}', '${password}', '${role.id}', '${createTime}', '${updateTime}')") + @Insert("INSERT INTO user (username, password, roleId, pushKey, createTime, updateTime) VALUES" + + "(#{username}, #{password}, #{role.id}, #{pushKey}, #{createTime}, #{updateTime})") int add(User user); @Update(value = {" "}) int update(User user); @@ -27,7 +28,7 @@ public interface UserMapper { @Delete("DELETE FROM user WHERE id != 1 and id=#{id}") int delete(int id); - @Select("select user.*, role.id roleID, role.name roleName, role.authority roleAuthority , role.createTime roleCreateTime , role.updateTime roleUpdateTime FROM user, role WHERE user.roleId=role.id and user.username=#{username} AND user.password=#{password}") + @Select("select u.*, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM user u, user_role r WHERE u.roleId=r.id and u.username=#{username} AND u.password=#{password}") @Results(id = "roleMap", value = { @Result(column = "roleID", property = "role.id"), @Result(column = "roleName", property = "role.name"), @@ -37,15 +38,28 @@ public interface UserMapper { }) User select(String username, String password); - @Select("select user.*, role.id roleID, role.name roleName, role.authority roleAuthority, role.createTime roleCreateTime , role.updateTime roleUpdateTime FROM user, role WHERE user.roleId=role.id and user.id=#{id}") + @Select("select u.*, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM user u, user_role r WHERE u.roleId=r.id and u.id=#{id}") @ResultMap(value="roleMap") User selectById(int id); - @Select("select user.*, role.id roleID, role.name roleName, role.authority roleAuthority, role.createTime roleCreateTime , role.updateTime roleUpdateTime FROM user, role WHERE user.roleId=role.id and username=#{username}") + @Select("select u.*, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM user u, user_role r WHERE u.roleId=r.id and u.username=#{username}") @ResultMap(value="roleMap") User getUserByUsername(String username); - @Select("select user.*, role.id roleID, role.name roleName, role.authority roleAuthority, role.createTime roleCreateTime , role.updateTime roleUpdateTime FROM user, role WHERE user.roleId=role.id") + @Select("select u.*, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM user u, user_role r WHERE u.roleId=r.id") @ResultMap(value="roleMap") List selectAll(); + + @Select("select * from (select user.*, concat(concat(#{callId}, '_'), pushKey) as str1 from user) as u where md5(u.str1) = #{sign}") + List checkPushAuthorityByCallIdAndSign(String callId, String sign); + + @Select("select * from user where md5(pushKey) = #{sign}") + List checkPushAuthorityByCallId(String sign); + + @Select("select u.id, u.username,u.pushKey,u.roleId, r.id as roleID, r.name as roleName, r.authority as roleAuthority , r.createTime as roleCreateTime , r.updateTime as roleUpdateTime FROM user u join user_role r on u.roleId=r.id") + @ResultMap(value="roleMap") + List getUsers(); + + @Update("update user set pushKey=#{pushKey} where id=#{id}") + int changePushKey(int id, String pushKey); } diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/ChannelSourceInfo.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/ChannelSourceInfo.java new file mode 100644 index 00000000..e8b91e7c --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/ChannelSourceInfo.java @@ -0,0 +1,22 @@ +package com.genersoft.iot.vmp.storager.dao.dto; + +public class ChannelSourceInfo { + private String name; + private int count; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/PlatformRegisterInfo.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/PlatformRegisterInfo.java new file mode 100644 index 00000000..16f66363 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/PlatformRegisterInfo.java @@ -0,0 +1,41 @@ +package com.genersoft.iot.vmp.storager.dao.dto; + +/** + * 平台发送注册/注销消息时缓存此消息 + * @author lin + */ +public class PlatformRegisterInfo { + + /** + * 平台Id + */ + private String platformId; + + /** + * 是否时注册,false为注销 + */ + private boolean register; + + public static PlatformRegisterInfo getInstance(String platformId, boolean register) { + PlatformRegisterInfo platformRegisterInfo = new PlatformRegisterInfo(); + platformRegisterInfo.setPlatformId(platformId); + platformRegisterInfo.setRegister(register); + return platformRegisterInfo; + } + + public String getPlatformId() { + return platformId; + } + + public void setPlatformId(String platformId) { + this.platformId = platformId; + } + + public boolean isRegister() { + return register; + } + + public void setRegister(boolean register) { + this.register = register; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/User.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/User.java index 950a8cae..c9b4002e 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/User.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/dto/User.java @@ -7,6 +7,7 @@ public class User { private String password; private String createTime; private String updateTime; + private String pushKey; private Role role; public int getId() { @@ -56,4 +57,12 @@ public class User { public void setRole(Role role) { this.role = role; } + + public String getPushKey() { + return pushKey; + } + + public void setPushKey(String pushKey) { + this.pushKey = pushKey; + } } 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 3ded4168..8cf5293b 100644 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java @@ -1,48 +1,62 @@ package com.genersoft.iot.vmp.storager.impl; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.common.SystemAllInfo; import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.conf.UserSetup; +import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.service.bean.MessageForPushChannel; import com.genersoft.iot.vmp.service.bean.ThirdPartyGB; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper; +import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.utils.SystemInfoUtils; import com.genersoft.iot.vmp.utils.redis.RedisUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import java.text.SimpleDateFormat; import java.util.*; @SuppressWarnings("rawtypes") @Component public class RedisCatchStorageImpl implements IRedisCatchStorage { - private Logger logger = LoggerFactory.getLogger(RedisCatchStorageImpl.class); - - @Autowired - private RedisUtil redis; + private final Logger logger = LoggerFactory.getLogger(RedisCatchStorageImpl.class); @Autowired private DeviceChannelMapper deviceChannelMapper; @Autowired - private UserSetup userSetup; - - private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private UserSetting userSetting; @Override - public Long getCSEQ(String method) { - String key = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetup.getServerId() + "_" + method; + public Long getCSEQ() { + String key = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId(); - long result = redis.incr(key, 1L); + long result = RedisUtil.incr(key, 1L); if (result > Integer.MAX_VALUE) { - redis.set(key, 1); + RedisUtil.set(key, 1); + result = 1; + } + return result; + } + + @Override + public Long getSN(String method) { + String key = VideoManagerConstants.SIP_SN_PREFIX + userSetting.getServerId() + "_" + method; + + long result = RedisUtil.incr(key, 1L); + if (result > Integer.MAX_VALUE) { + RedisUtil.set(key, 1); result = 1; } return result; @@ -50,11 +64,21 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void resetAllCSEQ() { - String scanKey = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetup.getServerId() + "_*"; - List keys = redis.scan(scanKey); - for (int i = 0; i < keys.size(); i++) { - String key = (String) keys.get(i); - redis.set(key, 1); + String scanKey = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId() + "_*"; + List keys = RedisUtil.scan(scanKey); + for (Object o : keys) { + String key = (String) o; + RedisUtil.set(key, 1); + } + } + + @Override + public void resetAllSN() { + String scanKey = VideoManagerConstants.SIP_SN_PREFIX + userSetting.getServerId() + "_*"; + List keys = RedisUtil.scan(scanKey); + for (Object o : keys) { + String key = (String) o; + RedisUtil.set(key, 1); } } @@ -65,7 +89,9 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { */ @Override public boolean startPlay(StreamInfo stream) { - return redis.set(String.format("%S_%S_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, userSetup.getServerId(), stream.getStreamId(),stream.getDeviceID(), stream.getChannelId()), + + return RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(), + stream.getMediaServerId(), stream.getStream(), stream.getDeviceID(), stream.getChannelId()), stream); } @@ -76,10 +102,13 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { */ @Override public boolean stopPlay(StreamInfo streamInfo) { - if (streamInfo == null) return false; - return redis.del(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - userSetup.getServerId(), - streamInfo.getStreamId(), + if (streamInfo == null) { + return false; + } + return RedisUtil.del(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, + userSetting.getServerId(), + streamInfo.getMediaServerId(), + streamInfo.getStream(), streamInfo.getDeviceID(), streamInfo.getChannelId())); } @@ -90,47 +119,45 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { */ @Override public StreamInfo queryPlay(StreamInfo streamInfo) { - return (StreamInfo)redis.get(String.format("%S_%s_%s_%s_%s", + return (StreamInfo)RedisUtil.get(String.format("%S_%s_%s_%s_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - userSetup.getServerId(), - streamInfo.getStreamId(), + userSetting.getServerId(), + streamInfo.getMediaServerId(), + streamInfo.getStream(), streamInfo.getDeviceID(), streamInfo.getChannelId())); } @Override public StreamInfo queryPlayByStreamId(String streamId) { - List playLeys = redis.scan(String.format("%S_%s_%s_*", VideoManagerConstants.PLAYER_PREFIX, userSetup.getServerId(), streamId)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); - } - - @Override - public StreamInfo queryPlaybackByStreamId(String streamId) { - List playLeys = redis.scan(String.format("%S_%s_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, userSetup.getServerId(), streamId)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); + List playLeys = RedisUtil.scan(String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(), streamId)); + if (playLeys == null || playLeys.size() == 0) { + return null; + } + return (StreamInfo)RedisUtil.get(playLeys.get(0).toString()); } @Override public StreamInfo queryPlayByDevice(String deviceId, String channelId) { -// List playLeys = redis.keys(String.format("%S_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - List playLeys = redis.scan(String.format("%S_%s_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, - userSetup.getServerId(), + List playLeys = RedisUtil.scan(String.format("%S_%s_*_*_%s_%s", VideoManagerConstants.PLAYER_PREFIX, + userSetting.getServerId(), deviceId, channelId)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); + if (playLeys == null || playLeys.size() == 0) { + return null; + } + return (StreamInfo)RedisUtil.get(playLeys.get(0).toString()); } @Override public Map queryPlayByDeviceId(String deviceId) { Map streamInfos = new HashMap<>(); -// List playLeys = redis.keys(String.format("%S_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, deviceId)); - List players = redis.scan(String.format("%S_%s_*_%S_*", VideoManagerConstants.PLAYER_PREFIX, userSetup.getServerId(),deviceId)); - if (players.size() == 0) return streamInfos; - for (int i = 0; i < players.size(); i++) { - String key = (String) players.get(i); - StreamInfo streamInfo = (StreamInfo)redis.get(key); + List players = RedisUtil.scan(String.format("%S_%s_*_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, userSetting.getServerId(),deviceId)); + if (players.size() == 0) { + return streamInfos; + } + for (Object player : players) { + String key = (String) player; + StreamInfo streamInfo = (StreamInfo) RedisUtil.get(key); streamInfos.put(streamInfo.getDeviceID() + "_" + streamInfo.getChannelId(), streamInfo); } return streamInfos; @@ -138,136 +165,306 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override - public boolean startPlayback(StreamInfo stream) { - return redis.set(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, userSetup.getServerId(),stream.getStreamId(), - stream.getDeviceID(), stream.getChannelId()), stream); + public boolean startPlayback(StreamInfo stream, String callId) { + return RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream); } @Override - public boolean startDownload(StreamInfo streamInfo) { - return redis.set(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, userSetup.getServerId(),streamInfo.getStreamId(), - streamInfo.getDeviceID(), streamInfo.getChannelId()), streamInfo); + public boolean startDownload(StreamInfo stream, String callId) { + boolean result; + if (stream.getProgress() == 1) { + result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, + userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream); + }else { + result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, + userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream, 60*60); + } + return result; } - @Override - public boolean stopPlayback(StreamInfo streamInfo) { - if (streamInfo == null) return false; - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(streamInfo.getDeviceID(), streamInfo.getChannelId()); + public boolean stopDownload(String deviceId, String channelId, String stream, String callId) { + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); if (deviceChannel != null) { deviceChannel.setStreamId(null); - deviceChannel.setDeviceId(streamInfo.getDeviceID()); + deviceChannel.setDeviceId(deviceId); deviceChannelMapper.update(deviceChannel); } - return redis.del(String.format("%S_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - userSetup.getServerId(), - streamInfo.getStreamId(), - streamInfo.getDeviceID(), - streamInfo.getChannelId())); + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List scan = RedisUtil.scan(key); + if (scan.size() > 0) { + for (Object keyObj : scan) { + RedisUtil.del((String) keyObj); + } + } + return true; } @Override - public StreamInfo queryPlaybackByDevice(String deviceId, String code) { - // String format = String.format("%S_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - // deviceId, - // code); - List playLeys = redis.scan(String.format("%S_%s_*_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - userSetup.getServerId(), - deviceId, - code)); - if (playLeys == null || playLeys.size() == 0) { - playLeys = redis.scan(String.format("%S_%s_*_*_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, - userSetup.getServerId(), - deviceId)); + public boolean stopPlayback(String deviceId, String channelId, String stream, String callId) { + DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); + if (deviceChannel != null) { + deviceChannel.setStreamId(null); + deviceChannel.setDeviceId(deviceId); + deviceChannelMapper.update(deviceChannel); } - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List scan = RedisUtil.scan(key); + if (scan.size() > 0) { + for (Object keyObj : scan) { + RedisUtil.del((String) keyObj); + } + } + return true; + } + + @Override + public StreamInfo queryPlayback(String deviceId, String channelId, String stream, String callId) { + if (stream == null && callId == null) { + return null; + } + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List streamInfoScan = RedisUtil.scan(key); + if (streamInfoScan.size() > 0) { + return (StreamInfo) RedisUtil.get((String) streamInfoScan.get(0)); + }else { + return null; + } + } + + @Override + public String queryPlaybackForKey(String deviceId, String channelId, String stream, String callId) { + if (stream == null && callId == null) { + return null; + } + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List streamInfoScan = RedisUtil.scan(key); + return (String) streamInfoScan.get(0); } @Override public void updatePlatformCatchInfo(ParentPlatformCatch parentPlatformCatch) { - String key = VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetup.getServerId() + "_" + parentPlatformCatch.getId(); - redis.set(key, parentPlatformCatch); - } - - @Override - public void updatePlatformKeepalive(ParentPlatform parentPlatform) { - String key = VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetup.getServerId() + "_" + parentPlatform.getServerGBId(); - redis.set(key, "", Integer.parseInt(parentPlatform.getKeepTimeout())); - } - - @Override - public void updatePlatformRegister(ParentPlatform parentPlatform) { - String key = VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetup.getServerId() + "_" + parentPlatform.getServerGBId(); - redis.set(key, "", Integer.parseInt(parentPlatform.getExpires())); + String key = VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + parentPlatformCatch.getId(); + RedisUtil.set(key, parentPlatformCatch); } @Override public ParentPlatformCatch queryPlatformCatchInfo(String platformGbId) { - return (ParentPlatformCatch)redis.get(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetup.getServerId() + "_" + platformGbId); + return (ParentPlatformCatch)RedisUtil.get(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + platformGbId); } @Override public void delPlatformCatchInfo(String platformGbId) { - redis.del(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetup.getServerId() + "_" + platformGbId); + RedisUtil.del(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + platformGbId); } @Override public void delPlatformKeepalive(String platformGbId) { - redis.del(VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetup.getServerId() + "_" + platformGbId); + RedisUtil.del(VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetting.getServerId() + "_" + platformGbId); } @Override public void delPlatformRegister(String platformGbId) { - redis.del(VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetup.getServerId() + "_" + platformGbId); + RedisUtil.del(VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetting.getServerId() + "_" + platformGbId); } @Override - public void updatePlatformRegisterInfo(String callId, String platformGbId) { - String key = VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetup.getServerId() + "_" + callId; - redis.set(key, platformGbId); + public void updatePlatformRegisterInfo(String callId, PlatformRegisterInfo platformRegisterInfo) { + String key = VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId; + RedisUtil.set(key, platformRegisterInfo, 30); } @Override - public String queryPlatformRegisterInfo(String callId) { - return (String)redis.get(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetup.getServerId() + "_" + callId); + public PlatformRegisterInfo queryPlatformRegisterInfo(String callId) { + return (PlatformRegisterInfo)RedisUtil.get(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId); } @Override public void delPlatformRegisterInfo(String callId) { - redis.del(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetup.getServerId() + "_" + callId); + RedisUtil.del(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId); } @Override public void cleanPlatformRegisterInfos() { - List regInfos = redis.scan(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetup.getServerId() + "_" + "*"); + List regInfos = RedisUtil.scan(VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + "*"); for (Object key : regInfos) { - redis.del(key.toString()); + RedisUtil.del(key.toString()); } } @Override public void updateSendRTPSever(SendRtpItem sendRtpItem) { - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + userSetup.getServerId() + "_" + sendRtpItem.getPlatformId() + "_" + sendRtpItem.getChannelId(); - redis.set(key, sendRtpItem); + + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_" + + sendRtpItem.getMediaServerId() + "_" + + sendRtpItem.getPlatformId() + "_" + + sendRtpItem.getChannelId() + "_" + + sendRtpItem.getStreamId() + "_" + + sendRtpItem.getCallId(); + RedisUtil.set(key, sendRtpItem); } @Override - public SendRtpItem querySendRTPServer(String platformGbId, String channelId) { - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + userSetup.getServerId() + "_" + platformGbId + "_" + channelId; - return (SendRtpItem)redis.get(key); + 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 + + userSetting.getServerId() + "_*_" + + platformGbId + "_" + + channelId + "_" + + streamId + "_" + + callId; + List scan = RedisUtil.scan(key); + if (scan.size() > 0) { + return (SendRtpItem)RedisUtil.get((String)scan.get(0)); + }else { + return null; + } + } + + @Override + public List querySendRTPServerByChnnelId(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(key); + List result = new ArrayList<>(); + for (Object o : scan) { + result.add((SendRtpItem) RedisUtil.get((String) 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(key); + List result = new ArrayList<>(); + for (Object o : scan) { + result.add((SendRtpItem) RedisUtil.get((String) o)); + } + return result; } @Override public List querySendRTPServer(String platformGbId) { - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + userSetup.getServerId() + "_" + platformGbId + "_*"; - List queryResult = redis.scan(key); + if (platformGbId == null) { + platformGbId = "*"; + } + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + + platformGbId + "_*" + "_*" + "_*"; + List queryResult = RedisUtil.scan(key); List result= new ArrayList<>(); - for (int i = 0; i < queryResult.size(); i++) { - String keyItem = (String) queryResult.get(i); - result.add((SendRtpItem)redis.get(keyItem)); + for (Object o : queryResult) { + String keyItem = (String) o; + result.add((SendRtpItem) RedisUtil.get(keyItem)); } return result; @@ -279,9 +476,40 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { * @param channelId */ @Override - public void deleteSendRTPServer(String platformGbId, String channelId) { - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + userSetup.getServerId() + "_" + platformGbId + "_" + channelId; - redis.del(key); + 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(key); + if (scan.size() > 0) { + for (Object keyStr : scan) { + RedisUtil.del((String)keyStr); + } + } + } + + @Override + public List queryAllSendRTPServer() { + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*"; + List queryResult = RedisUtil.scan(key); + List result= new ArrayList<>(); + + for (Object o : queryResult) { + String keyItem = (String) o; + result.add((SendRtpItem) RedisUtil.get(keyItem)); + } + + return result; } /** @@ -290,8 +518,10 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { */ @Override public boolean isChannelSendingRTP(String channelId) { - String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + userSetup.getServerId() + "_" + "*_" + channelId; - List RtpStreams = redis.scan(key); + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_*_" + + channelId + "*_" + "*_"; + List RtpStreams = RedisUtil.scan(key); if (RtpStreams.size() > 0) { return true; } else { @@ -301,114 +531,347 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void clearCatchByDeviceId(String deviceId) { - List playLeys = redis.scan(String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, - userSetup.getServerId(), + List playLeys = RedisUtil.scan(String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAYER_PREFIX, + userSetting.getServerId(), deviceId)); if (playLeys.size() > 0) { for (Object key : playLeys) { - redis.del(key.toString()); + RedisUtil.del(key.toString()); } } - List playBackers = redis.scan(String.format("%S_%s_*_%s_*", VideoManagerConstants.PLAY_BLACK_PREFIX, - userSetup.getServerId(), + List playBackers = RedisUtil.scan(String.format("%S_%s_*_%s_*_*_*", VideoManagerConstants.PLAY_BLACK_PREFIX, + userSetting.getServerId(), deviceId)); if (playBackers.size() > 0) { for (Object key : playBackers) { - redis.del(key.toString()); + RedisUtil.del(key.toString()); } } - } - @Override - public void outlineForAll() { - List onlineDevices = redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + "*" ); - for (int i = 0; i < onlineDevices.size(); i++) { - String key = (String) onlineDevices.get(i); - redis.del(key); + List deviceCache = RedisUtil.scan(String.format("%S%s_%s", VideoManagerConstants.DEVICE_PREFIX, + userSetting.getServerId(), + deviceId)); + if (deviceCache.size() > 0) { + for (Object key : deviceCache) { + RedisUtil.del(key.toString()); + } } } - @Override - public List getOnlineForAll() { - List result = new ArrayList<>(); - List onlineDevices = redis.scan(VideoManagerConstants.KEEPLIVEKEY_PREFIX + userSetup.getServerId() + "_" + "*" ); - for (int i = 0; i < onlineDevices.size(); i++) { - String key = (String) onlineDevices.get(i); - result.add((String) redis.get(key)); - } - return result; - } - @Override public void updateWVPInfo(JSONObject jsonObject, int time) { - String key = VideoManagerConstants.WVP_SERVER_PREFIX + userSetup.getServerId(); - redis.set(key, jsonObject, time); + String key = VideoManagerConstants.WVP_SERVER_PREFIX + userSetting.getServerId(); + RedisUtil.set(key, jsonObject, time); } @Override public void sendStreamChangeMsg(String type, JSONObject jsonObject) { String key = VideoManagerConstants.WVP_MSG_STREAM_CHANGE_PREFIX + type; - logger.debug("[redis 流变化事件] {}: {}", key, jsonObject.toString()); - redis.convertAndSend(key, jsonObject); + logger.info("[redis 流变化事件] {}: {}", key, jsonObject.toString()); + RedisUtil.convertAndSend(key, jsonObject); } @Override - public void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, StreamInfo streamInfo) { - String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetup.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerItem.getId(); - redis.set(key, streamInfo); + public void addStream(MediaServerItem mediaServerItem, String type, String app, String streamId, OnStreamChangedHookParam onStreamChangedHookParam) { + // 查找是否使用了callID + StreamAuthorityInfo streamAuthorityInfo = getStreamAuthorityInfo(app, streamId); + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerItem.getId(); + if (streamAuthorityInfo != null) { + onStreamChangedHookParam.setCallId(streamAuthorityInfo.getCallId()); + } + RedisUtil.set(key, onStreamChangedHookParam); } @Override public void removeStream(String mediaServerId, String type, String app, String streamId) { - String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetup.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerId; - redis.del(key); + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerId; + RedisUtil.del(key); } @Override - public StreamInfo queryDownloadByStreamId(String streamId) { - List playLeys = redis.scan(String.format("%S_%s_%s_*", VideoManagerConstants.DOWNLOAD_PREFIX, userSetup.getServerId(), streamId)); - if (playLeys == null || playLeys.size() == 0) return null; - return (StreamInfo)redis.get(playLeys.get(0).toString()); + public StreamInfo queryDownload(String deviceId, String channelId, String stream, String callId) { + if (stream == null && callId == null) { + return null; + } + if (deviceId == null) { + deviceId = "*"; + } + if (channelId == null) { + channelId = "*"; + } + if (stream == null) { + stream = "*"; + } + if (callId == null) { + callId = "*"; + } + String key = String.format("%S_%s_*_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX, + userSetting.getServerId(), + deviceId, + channelId, + stream, + callId + ); + List streamInfoScan = RedisUtil.scan(key); + if (streamInfoScan.size() > 0) { + return (StreamInfo) RedisUtil.get((String) streamInfoScan.get(0)); + }else { + return null; + } } @Override public ThirdPartyGB queryMemberNoGBId(String queryKey) { String key = VideoManagerConstants.WVP_STREAM_GB_ID_PREFIX + queryKey; - JSONObject jsonObject = (JSONObject)redis.get(key); - return JSONObject.toJavaObject(jsonObject, ThirdPartyGB.class); + JSONObject jsonObject = (JSONObject)RedisUtil.get(key); + return jsonObject.to(ThirdPartyGB.class); } @Override public void removeStream(String mediaServerId, String type) { - String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetup.getServerId() + "_" + type + "_*_*_" + mediaServerId; - List streams = redis.scan(key); + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_*_*_" + mediaServerId; + List streams = RedisUtil.scan(key); for (Object stream : streams) { - redis.del((String) stream); + RedisUtil.del((String) stream); } } @Override - public List getStreams(String mediaServerId, String type) { - List result = new ArrayList<>(); - String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetup.getServerId() + "_" + type + "_*_*_" + mediaServerId; - List streams = redis.scan(key); + public List getStreams(String mediaServerId, String type) { + List result = new ArrayList<>(); + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_*_*_" + mediaServerId; + List streams = RedisUtil.scan(key); for (Object stream : streams) { - StreamInfo streamInfo = (StreamInfo)redis.get((String) stream); - result.add(streamInfo); + OnStreamChangedHookParam onStreamChangedHookParam = (OnStreamChangedHookParam)RedisUtil.get((String) stream); + result.add(onStreamChangedHookParam); } return result; } @Override public void updateDevice(Device device) { - String key = VideoManagerConstants.DEVICE_PREFIX + userSetup.getServerId() + "_" + device.getDeviceId(); - redis.set(key, device); + String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + device.getDeviceId(); + RedisUtil.set(key, device); + } + + @Override + public void removeDevice(String deviceId) { + String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; + RedisUtil.del(key); } @Override public Device getDevice(String deviceId) { - String key = VideoManagerConstants.DEVICE_PREFIX + userSetup.getServerId() + "_" + deviceId; - return (Device)redis.get(key); + String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; + return (Device)RedisUtil.get(key); + } + + @Override + public void updateGpsMsgInfo(GPSMsgInfo gpsMsgInfo) { + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gpsMsgInfo.getId(); + RedisUtil.set(key, gpsMsgInfo, 60); // 默认GPS消息保存1分钟 + } + + @Override + public GPSMsgInfo getGpsMsgInfo(String gbId) { + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gbId; + return (GPSMsgInfo)RedisUtil.get(key); + } + + @Override + public List getAllGpsMsgInfo() { + String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*"; + List result = new ArrayList<>(); + List keys = RedisUtil.scan(scanKey); + for (Object o : keys) { + String key = (String) o; + GPSMsgInfo gpsMsgInfo = (GPSMsgInfo) RedisUtil.get(key); + if (!gpsMsgInfo.isStored()) { // 只取没有存过得 + result.add((GPSMsgInfo) RedisUtil.get(key)); + } + } + + return result; + } + + @Override + public void updateStreamAuthorityInfo(String app, String stream, StreamAuthorityInfo streamAuthorityInfo) { + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream; + RedisUtil.set(key, streamAuthorityInfo); + } + + @Override + public void removeStreamAuthorityInfo(String app, String stream) { + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ; + RedisUtil.del(key); + } + + @Override + public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) { + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ; + return (StreamAuthorityInfo) RedisUtil.get(key); + + } + + @Override + public List getAllStreamAuthorityInfo() { + String scanKey = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_*_*" ; + List result = new ArrayList<>(); + List keys = RedisUtil.scan(scanKey); + for (Object o : keys) { + String key = (String) o; + result.add((StreamAuthorityInfo) RedisUtil.get(key)); + } + return result; + } + + + @Override + public OnStreamChangedHookParam getStreamInfo(String app, String streamId, String mediaServerId) { + String scanKey = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_*_" + app + "_" + streamId + "_" + mediaServerId; + + OnStreamChangedHookParam result = null; + List keys = RedisUtil.scan(scanKey); + if (keys.size() > 0) { + String key = (String) keys.get(0); + result = (OnStreamChangedHookParam)RedisUtil.get(key); + } + + return result; + } + + @Override + public void addCpuInfo(double cpuInfo) { + String key = VideoManagerConstants.SYSTEM_INFO_CPU_PREFIX + userSetting.getServerId(); + Map infoMap = new HashMap<>(); + infoMap.put("time", DateUtil.getNow()); + infoMap.put("data", cpuInfo + ""); + RedisUtil.lSet(key, infoMap); + // 每秒一个,最多只存30个 + if (RedisUtil.lGetListSize(key) >= 30) { + for (int i = 0; i < RedisUtil.lGetListSize(key) - 30; i++) { + RedisUtil.lLeftPop(key); + } + } + } + + @Override + public void addMemInfo(double memInfo) { + String key = VideoManagerConstants.SYSTEM_INFO_MEM_PREFIX + userSetting.getServerId(); + Map infoMap = new HashMap<>(); + infoMap.put("time", DateUtil.getNow()); + infoMap.put("data", memInfo + ""); + RedisUtil.lSet(key, infoMap); + // 每秒一个,最多只存30个 + if (RedisUtil.lGetListSize(key) >= 30) { + for (int i = 0; i < RedisUtil.lGetListSize(key) - 30; i++) { + RedisUtil.lLeftPop(key); + } + } + } + + @Override + public void addNetInfo(Map networkInterfaces) { + String key = VideoManagerConstants.SYSTEM_INFO_NET_PREFIX + userSetting.getServerId(); + Map infoMap = new HashMap<>(); + infoMap.put("time", DateUtil.getNow()); + for (String netKey : networkInterfaces.keySet()) { + infoMap.put(netKey, networkInterfaces.get(netKey)); + } + RedisUtil.lSet(key, infoMap); + // 每秒一个,最多只存30个 + if (RedisUtil.lGetListSize(key) >= 30) { + for (int i = 0; i < RedisUtil.lGetListSize(key) - 30; i++) { + RedisUtil.lLeftPop(key); + } + } + } + + @Override + public void addDiskInfo(List> diskInfo) { + + String key = VideoManagerConstants.SYSTEM_INFO_DISK_PREFIX + userSetting.getServerId(); + RedisUtil.set(key, diskInfo); + } + + @Override + public SystemAllInfo getSystemInfo() { + String cpuKey = VideoManagerConstants.SYSTEM_INFO_CPU_PREFIX + userSetting.getServerId(); + String memKey = VideoManagerConstants.SYSTEM_INFO_MEM_PREFIX + userSetting.getServerId(); + String netKey = VideoManagerConstants.SYSTEM_INFO_NET_PREFIX + userSetting.getServerId(); + String diskKey = VideoManagerConstants.SYSTEM_INFO_DISK_PREFIX + userSetting.getServerId(); + SystemAllInfo systemAllInfo = new SystemAllInfo(); + systemAllInfo.setCpu(RedisUtil.lGet(cpuKey, 0, -1)); + systemAllInfo.setMem(RedisUtil.lGet(memKey, 0, -1)); + systemAllInfo.setNet(RedisUtil.lGet(netKey, 0, -1)); + + systemAllInfo.setDisk(RedisUtil.get(diskKey)); + systemAllInfo.setNetTotal(SystemInfoUtils.getNetworkTotal()); + return systemAllInfo; + } + + @Override + public void sendMobilePositionMsg(JSONObject jsonObject) { + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_MOBILE_POSITION; + logger.info("[redis发送通知] 移动位置 {}: {}", key, jsonObject.toString()); + RedisUtil.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()); + RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg)); + } + + @Override + public void sendAlarmMsg(AlarmChannelMessage msg) { + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM; + logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg)); + RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg)); + } + + @Override + public boolean deviceIsOnline(String deviceId) { + return getDevice(deviceId).getOnline() == 1; + } + + + @Override + public void sendStreamPushRequestedMsgForStatus() { + String key = VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED; + logger.info("[redis通知]获取所有推流设备的状态"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put(key, key); + RedisUtil.convertAndSend(key, jsonObject); + } + + @Override + public int getPushStreamCount(String id) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_PUSH_*_*_" + id; + return RedisUtil.scan(key).size(); + } + + @Override + public int getProxyStreamCount(String id) { + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_PULL_*_*_" + id; + return RedisUtil.scan(key).size(); + } + + @Override + public int getGbReceiveCount(String id) { + String playKey = VideoManagerConstants.PLAYER_PREFIX + "_" + userSetting.getServerId() + "_" + id + "_*"; + String playBackKey = VideoManagerConstants.PLAY_BLACK_PREFIX + "_" + userSetting.getServerId() + "_" + id + "_*"; + String downloadKey = VideoManagerConstants.DOWNLOAD_PREFIX + "_" + userSetting.getServerId() + "_" + id + "_*"; + + return RedisUtil.scan(playKey).size() + RedisUtil.scan(playBackKey).size() + RedisUtil.scan(downloadKey).size(); + } + + @Override + public int getGbSendCount(String id) { + String key = VideoManagerConstants.PLATFORM_SEND_RTP_INFO_PREFIX + + userSetting.getServerId() + "_*_" + id + "_*"; + return RedisUtil.scan(key).size(); } } 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 new file mode 100644 index 00000000..41cabad8 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java @@ -0,0 +1,992 @@ +package com.genersoft.iot.vmp.storager.impl; + +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.IGbStreamService; +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.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 +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 StreamProxyMapper streamProxyMapper; + + @Autowired + private StreamPushMapper streamPushMapper; + + @Autowired + private GbStreamMapper gbStreamMapper; + + @Autowired + private UserSetting userSetting; + + @Autowired + private PlatformCatalogMapper catalogMapper; + + @Autowired + private PlatformGbStreamMapper platformGbStreamMapper; + + @Autowired + private IGbStreamService gbStreamService; + + @Autowired + private ParentPlatformMapper parentPlatformMapper; + + /** + * 根据设备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<>(); + 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()); + if (allChannelMap.containsKey(deviceChannel.getChannelId())) { + deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); + deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio()); + } + 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 cleanChannelsResult = deviceChannelMapper.cleanChannelsNotInList(deviceId, channels); + int limitCount = 300; + boolean result = cleanChannelsResult < 0; + if (!result && channels.size() > 0) { + 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 = result || deviceChannelMapper.batchAdd(channels.subList(i, toIndex)) < 0; + } + }else { + result = result || deviceChannelMapper.batchAdd(channels) < 0; + } + } + if (result) { + //事务回滚 + dataSourceTransactionManager.rollback(transactionStatus); + } + dataSourceTransactionManager.commit(transactionStatus); //手动提交 + return true; + }catch (Exception e) { + e.printStackTrace(); + 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); + } + } + List addChannels = new ArrayList<>(); + List updateChannels = new ArrayList<>(); + + + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + // 数据去重 + 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()); + if (allChannelMap.containsKey(deviceChannel.getChannelId())) { + deviceChannel.setStreamId(allChannelMap.get(deviceChannel.getChannelId()).getStreamId()); + deviceChannel.setHasAudio(allChannelMap.get(deviceChannel.getChannelId()).isHasAudio()); + updateChannels.add(deviceChannel); + }else { + addChannels.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 (addChannels.size() > 0) { + for (DeviceChannel channel : addChannels) { + if (subContMap.get(channel.getChannelId()) != null){ + channel.setSubCount(subContMap.get(channel.getChannelId())); + } + } + } + if (updateChannels.size() > 0) { + for (DeviceChannel channel : updateChannels) { + if (subContMap.get(channel.getChannelId()) != null){ + channel.setSubCount(subContMap.get(channel.getChannelId())); + } + } + } + + } + if (stringBuilder.length() > 0) { + logger.info("[目录查询]收到的数据存在重复: {}" , stringBuilder); + } + if(CollectionUtils.isEmpty(updateChannels) && CollectionUtils.isEmpty(addChannels) ){ + logger.info("通道更新,数据为空={}" , deviceChannelList); + return false; + } + try { + int limitCount = 300; + 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); + }else { + //手动提交 + dataSourceTransactionManager.commit(transactionStatus); + } + return true; + }catch (Exception e) { + e.printStackTrace(); + 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, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List channelIds) { + return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, null, query, hasSubChannel, online, start, limit,channelIds); + } + + + @Override + public List queryChannelsByDeviceId(String deviceId,Boolean online,List channelIds) { + return deviceChannelMapper.queryChannels(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) { + if (mobilePosition.getDeviceId().equals(mobilePosition.getChannelId())) { + mobilePosition.setChannelId(null); + } + 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 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 (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)) { + if (platform.getDeviceGBId().equals(platformCatalog.getParentId())) { + // 第一层节点 + platformCatalog.setBusinessGroupId(platformCatalog.getId()); + platformCatalog.setParentId(platform.getDeviceGBId()); + }else { + // 获取顶层的 + PlatformCatalog topCatalog = getTopCatalog(platformCatalog.getParentId(), platform.getDeviceGBId()); + platformCatalog.setBusinessGroupId(topCatalog.getId()); + } + } + if (platform.getTreeType().equals(TreeType.CIVIL_CODE)) { + platformCatalog.setCivilCode(platformCatalog.getId()); + if (platformCatalog.getPlatformId().equals(platformCatalog.getParentId())) { + // 第一层节点 + platformCatalog.setParentId(platform.getDeviceGBId()); + } + } + + 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.selectParentCatalog(id); + if (catalog.getParentId().equals(platformId)) { + return catalog; + }else { + return getTopCatalog(catalog.getParentId(), platformId); + } + } + + @Override + public PlatformCatalog getCatalog(String id) { + return catalogMapper.select(id); + } + + @Override + public int delCatalog(String id) { + PlatformCatalog platformCatalog = catalogMapper.select(id); + if (platformCatalog.getChildrenCount() > 0) { + List platformCatalogList = catalogMapper.selectByParentId(platformCatalog.getPlatformId(), platformCatalog.getId()); + for (PlatformCatalog catalog : platformCatalogList) { + if (catalog.getChildrenCount() == 0) { + delCatalogExecute(catalog.getId(), catalog.getPlatformId()); + }else { + delCatalog(catalog.getId()); + } + } + } + return delCatalogExecute(id, platformCatalog.getPlatformId()); + } + private int delCatalogExecute(String id, String platformId) { + int delresult = catalogMapper.del(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.delByCatalogId(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(id); + return delresult + delChannelresult + delStreamresult; + } + + + @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.setLongitude(0.0); + deviceChannel.setLatitude(0.0); + deviceChannel.setDeviceId(platform.getDeviceGBId()); + deviceChannel.setManufacture("wvp-pro"); + deviceChannel.setStatus(1); + deviceChannel.setParental(1); + + deviceChannel.setRegisterWay(1); + // 行政区划应该是Domain的前八位 + if (platform.getTreeType().equals(TreeType.BUSINESS_GROUP)) { + 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/storager/impl/VideoManagerStoragerImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java deleted file mode 100644 index f1a9f9a5..00000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStoragerImpl.java +++ /dev/null @@ -1,769 +0,0 @@ -package com.genersoft.iot.vmp.storager.impl; - -import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; -import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; -import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; -import com.genersoft.iot.vmp.service.IGbStreamService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.storager.dao.*; -import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce; -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.StringUtils; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @description:视频设备数据存储-jdbc实现 - * @author: swwheihei - * @date: 2020年5月6日 下午2:31:42 - */ -@SuppressWarnings("rawtypes") -@Component -public class VideoManagerStoragerImpl implements IVideoManagerStorager { - - private Logger logger = LoggerFactory.getLogger(VideoManagerStoragerImpl.class); - - @Autowired - DataSourceTransactionManager dataSourceTransactionManager; - - @Autowired - TransactionDefinition transactionDefinition; - - @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 StreamProxyMapper streamProxyMapper; - - @Autowired - private StreamPushMapper streamPushMapper; - - @Autowired - private GbStreamMapper gbStreamMapper; -; - - @Autowired - private PlatformGbStreamMapper platformGbStreamMapper; - - @Autowired - private IGbStreamService gbStreamService; - - @Autowired - private ParentPlatformMapper parentPlatformMapper; - - @Autowired - private VideoStreamSessionManager streamSession; - - @Autowired - private MediaServerMapper mediaServerMapper; - - private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - - /** - * 根据设备ID判断设备是否存在 - * - * @param deviceId 设备ID - * @return true:存在 false:不存在 - */ - @Override - public boolean exists(String deviceId) { - return deviceMapper.getDeviceByDeviceId(deviceId) != null; - } - - /** - * 视频设备创建 - * - * @param device 设备对象 - * @return true:创建成功 false:创建失败 - */ - @Override - public synchronized boolean create(Device device) { - redisCatchStorage.updateDevice(device); - return deviceMapper.add(device) > 0; - } - - - - /** - * 视频设备更新 - * - * @param device 设备对象 - * @return true:更新成功 false:更新失败 - */ - @Override - public synchronized boolean updateDevice(Device device) { - String now = this.format.format(System.currentTimeMillis()); - device.setUpdateTime(now); - Device deviceByDeviceId = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); - if (deviceByDeviceId == null) { - device.setCreateTime(now); - redisCatchStorage.updateDevice(device); - return deviceMapper.add(device) > 0; - }else { - redisCatchStorage.updateDevice(device); - return deviceMapper.update(device) > 0; - } - - - } - - @Override - public synchronized void updateChannel(String deviceId, DeviceChannel channel) { - String channelId = channel.getChannelId(); - channel.setDeviceId(deviceId); - channel.setStreamId(streamSession.getStreamId(deviceId, channel.getChannelId())); - String now = this.format.format(System.currentTimeMillis()); - channel.setUpdateTime(now); - DeviceChannel deviceChannel = deviceChannelMapper.queryChannel(deviceId, channelId); - if (deviceChannel == null) { - channel.setCreateTime(now); - deviceChannelMapper.add(channel); - }else { - deviceChannelMapper.update(channel); - } - } - - @Override - public void updateChannels(String deviceId, List channels) { - List addChannels = new ArrayList<>(); - List updateChannels = new ArrayList<>(); - HashMap channelsInStore = new HashMap<>(); - if (channels != null && channels.size() > 0) { - List channelList = deviceChannelMapper.queryChannelsByDeviceId(deviceId); - if (channelList.size() == 0) { - for (DeviceChannel channel : channels) { - channel.setDeviceId(deviceId); - channel.setStreamId(streamSession.getStreamId(deviceId, channel.getChannelId())); - String now = this.format.format(System.currentTimeMillis()); - channel.setUpdateTime(now); - channel.setCreateTime(now); - addChannels.add(channel); - } - }else { - for (DeviceChannel deviceChannel : channelList) { - channelsInStore.put(deviceChannel.getChannelId(), deviceChannel); - } - for (DeviceChannel channel : channels) { - String channelId = channel.getChannelId(); - channel.setDeviceId(deviceId); - channel.setStreamId(streamSession.getStreamId(deviceId, channel.getChannelId())); - String now = this.format.format(System.currentTimeMillis()); - channel.setUpdateTime(now); - if (channelsInStore.get(channel.getChannelId()) != null) { - updateChannels.add(channel); - }else { - addChannels.add(channel); - channel.setCreateTime(now); - } - } - } - int limitCount = 300; - 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(); - } - deviceChannelMapper.batchAdd(addChannels.subList(i, toIndex)); - } - }else { - deviceChannelMapper.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(); - } - deviceChannelMapper.batchAdd(updateChannels.subList(i, toIndex)); - } - }else { - deviceChannelMapper.batchUpdate(updateChannels); - } - } - } - } - - @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, int page, int count) { - // 获取到所有正在播放的流 - PageHelper.startPage(page, count); - List all = deviceChannelMapper.queryChannels(deviceId, null, query, hasSubChannel, online); - return new PageInfo<>(all); - } - - @Override - public List queryChannelsByDeviceId(String deviceId) { - return deviceChannelMapper.queryChannels(deviceId, null,null, null, null); - } - - @Override - public PageInfo querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, String online, int page, int count) { - PageHelper.startPage(page, count); - List all = deviceChannelMapper.queryChannels(deviceId, parentChannelId, null, null, 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) { - PageHelper.startPage(page, count); - List all = deviceMapper.getDevices(); - return new PageInfo<>(all); - } - - /** - * 获取多个设备 - * - * @return List 设备对象数组 - */ - @Override - public List queryVideoDeviceList() { - - List deviceList = deviceMapper.getDevices(); - return deviceList; - } - - /** - * 删除设备 - * - * @param deviceId 设备ID - * @return true:删除成功 false:删除失败 - */ - @Override - public boolean delete(String deviceId) { - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - boolean result = false; - try { - if (platformChannelMapper.delChannelForDeviceId(deviceId) <0 // 删除与国标平台的关联 - || deviceChannelMapper.cleanChannelsByDeviceId(deviceId) < 0 // 删除他的通道 - || deviceMapper.del(deviceId) < 0 // 移除设备信息 - ) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - } - result = true; - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - }catch (Exception e) { - dataSourceTransactionManager.rollback(transactionStatus); - } - return result; - } - - /** - * 更新设备在线 - * - * @param deviceId 设备ID - * @return true:更新成功 false:更新失败 - */ - @Override - public synchronized boolean online(String deviceId) { - Device device = deviceMapper.getDeviceByDeviceId(deviceId); - if (device == null) { - return false; - } - device.setOnline(1); - logger.info("更新设备在线: " + deviceId); - redisCatchStorage.updateDevice(device); - return deviceMapper.update(device) > 0; - } - - /** - * 更新设备离线 - * - * @param deviceId 设备ID - * @return true:更新成功 false:更新失败 - */ - @Override - public synchronized boolean outline(String deviceId) { - logger.info("更新设备离线: " + deviceId); - Device device = deviceMapper.getDeviceByDeviceId(deviceId); - if (device == null) return false; - device.setOnline(0); - redisCatchStorage.updateDevice(device); - return deviceMapper.update(device) > 0; - } - - /** - * 更新所有设备离线 - * - * @return true:更新成功 false:更新失败 - */ - @Override - public synchronized boolean outlineForAll() { - logger.info("更新所有设备离线"); - int result = deviceMapper.outlineForAll(); - return result > 0; - } - - /** - * 清空通道 - * @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 startTime, String endTime) { - return deviceMobilePositionMapper.queryPositionByDeviceIdAndTime(deviceId, startTime, endTime); - } - - @Override - public boolean addParentPlatform(ParentPlatform parentPlatform) { - int result = platformMapper.addParentPlatform(parentPlatform); - return result > 0; - } - - @Override - public boolean updateParentPlatform(ParentPlatform parentPlatform) { - int result = 0; - ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId()); // .getDeviceGBId()); - if (parentPlatform.getId() == null ) { - 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 = redisCatchStorage.queryPlatformCatchInfo(parentPlatById.getServerGBId()); - parentPlatformCatch.setId(parentPlatform.getServerGBId()); - redisCatchStorage.delPlatformCatchInfo(parentPlatById.getServerGBId()); - } - result = platformMapper.updateParentPlatform(parentPlatform); - } - // 更新缓存 - parentPlatformCatch.setParentPlatform(parentPlatform); - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch); - // 共享所有视频流,需要将现有视频流添加到此平台 - List gbStreams = gbStreamMapper.selectAll(); - if (gbStreams.size() > 0) { - if (parentPlatform.isShareAllLiveStream()) { - gbStreamService.addPlatformInfo(gbStreams, parentPlatform.getServerGBId()); - }else { - gbStreamService.delPlatformInfo(gbStreams); - } - } - return result > 0; - } - - @Transactional - @Override - public boolean deleteParentPlatform(ParentPlatform parentPlatform) { - int result = platformMapper.delParentPlatform(parentPlatform); - // 删除关联的通道 - platformChannelMapper.cleanChannelForGB(parentPlatform.getServerGBId()); - return result > 0; - } - - @Override - public PageInfo queryParentPlatformList(int page, int count) { - PageHelper.startPage(page, count); - List all = platformMapper.getParentPlatformList(); - return new PageInfo<>(all); - } - - @Override - public ParentPlatform queryParentPlatByServerGBId(String platformGbId) { - return platformMapper.getParentPlatByServerGBId(platformGbId); - } - - @Override - public List queryEnableParentPlatformList(boolean enable) { - return platformMapper.getEnableParentPlatformList(enable); - } - - @Override - public void outlineForAllParentPlatform() { - platformMapper.outlineForAllParentPlatform(); - } - - - @Override - public PageInfo queryAllChannelList(int page, int count, String query, Boolean online, - Boolean channelType, String platformId, Boolean inPlatform) { - PageHelper.startPage(page, count); - List all = deviceChannelMapper.queryChannelListInAll(query, online, channelType, platformId, inPlatform); - return new PageInfo<>(all); - } - - @Override - public List queryChannelListInParentPlatform(String platformId) { - - return deviceChannelMapper.queryChannelListInAll(null, null, null, platformId, true); - } - - @Override - public int updateChannelForGB(String platformId, List channelReduces) { - - Map deviceAndChannels = new HashMap<>(); - for (ChannelReduce channelReduce : channelReduces) { - deviceAndChannels.put(channelReduce.getDeviceId() + "_" + channelReduce.getChannelId(), channelReduce); - } - List deviceAndChannelList = new ArrayList<>(deviceAndChannels.keySet()); - // 查询当前已经存在的 - List relatedPlatformchannels = platformChannelMapper.findChannelRelatedPlatform(platformId, deviceAndChannelList); - if (relatedPlatformchannels != null) { - deviceAndChannelList.removeAll(relatedPlatformchannels); - } - for (String relatedPlatformchannel : relatedPlatformchannels) { - deviceAndChannels.remove(relatedPlatformchannel); - } - List channelReducesToAdd = new ArrayList<>(deviceAndChannels.values()); - // 对剩下的数据进行存储 - int result = 0; - if (channelReducesToAdd.size() > 0) { - result = platformChannelMapper.addChannels(platformId, channelReducesToAdd); - } - - return result; - } - - - @Override - public int delChannelForGB(String platformId, List channelReduces) { - - int result = platformChannelMapper.delChannelForGB(platformId, channelReduces); - - return result; - } - - @Override - public DeviceChannel queryChannelInParentPlatform(String platformId, String channelId) { - DeviceChannel channel = platformChannelMapper.queryChannelInParentPlatform(platformId, channelId); - return channel; - } - - @Override - public Device queryVideoDeviceByPlatformIdAndChannelId(String platformId, String channelId) { - Device device = platformChannelMapper.queryVideoDeviceByPlatformIdAndChannelId(platformId, channelId); - return device; - } - - /** - * 查询最新移动位置 - * @param deviceId - */ - @Override - public MobilePosition queryLatestPosition(String deviceId) { - return deviceMobilePositionMapper.queryLatestPositionByDevice(deviceId); - } - - /** - * 删除指定设备的所有移动位置 - * @param deviceId - */ - public int clearMobilePositionsByDeviceId(String deviceId) { - return deviceMobilePositionMapper.clearMobilePositionsByDeviceId(deviceId); - } - - /** - * 新增代理流 - * @param streamProxyItem - * @return - */ - @Override - public boolean addStreamProxy(StreamProxyItem streamProxyItem) { - TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); - boolean result = false; - streamProxyItem.setStreamType("proxy"); - streamProxyItem.setStatus(true); - String now = this.format.format(System.currentTimeMillis()); - streamProxyItem.setCreateTime(now); - try { - if (gbStreamMapper.add(streamProxyItem)<0 || streamProxyMapper.add(streamProxyItem) < 0) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - } - 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 (gbStreamMapper.update(streamProxyItem)<0 || streamProxyMapper.update(streamProxyItem) < 0) { - //事务回滚 - dataSourceTransactionManager.rollback(transactionStatus); - } - dataSourceTransactionManager.commit(transactionStatus); //手动提交 - result = true; - }catch (Exception e) { - e.printStackTrace(); - dataSourceTransactionManager.rollback(transactionStatus); - } - return result; - } - - /** - * 移除代理流 - * @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 List queryStreamInParentPlatform(String platformId, String gbId) { - return gbStreamMapper.queryStreamInPlatform(platformId, gbId); - } - - /** - * 获取平台关联的直播流 - * @param platformId - * @return - */ - @Override - public List queryGbStreamListInPlatform(String platformId) { - return gbStreamMapper.queryGbStreamListInPlatform(platformId); - } - - /** - * 按照是app和stream获取代理流 - * @param app - * @param stream - * @return - */ - @Override - public StreamProxyItem queryStreamProxy(String app, String stream){ - return streamProxyMapper.selectOne(app, stream); - } - - @Override - public void updateMediaList(List streamPushItems) { - if (streamPushItems == null || streamPushItems.size() == 0) return; - logger.info("updateMediaList: " + streamPushItems.size()); - streamPushMapper.addAll(streamPushItems); - // TODO 待优化 - for (int i = 0; i < streamPushItems.size(); i++) { - gbStreamMapper.setStatus(streamPushItems.get(i).getApp(), streamPushItems.get(i).getStream(), true); - } - } - - @Override - public void updateMedia(StreamPushItem streamPushItem) { - streamPushMapper.del(streamPushItem.getApp(), streamPushItem.getStream()); - streamPushMapper.add(streamPushItem); - gbStreamMapper.setStatus(streamPushItem.getApp(), streamPushItem.getStream(), true); - if(!StringUtils.isEmpty(streamPushItem.getGbId() )){ - // 查找开启了全部直播流共享的上级平台 - List parentPlatforms = parentPlatformMapper.selectAllAhareAllLiveStream(); - if (parentPlatforms.size() > 0) { - for (ParentPlatform parentPlatform : parentPlatforms) { - streamPushItem.setPlatformId(parentPlatform.getServerGBId()); - String stream = streamPushItem.getStream(); - StreamProxyItem streamProxyItems = platformGbStreamMapper.selectOne(streamPushItem.getApp(), stream, parentPlatform.getServerGBId()); - if (streamProxyItems == null) { - platformGbStreamMapper.add(streamPushItem); - } - - } - } - } - - } - - @Override - public int removeMedia(String app, String stream) { - return streamPushMapper.del(app, stream); - } - - @Override - public void clearMediaList() { - streamPushMapper.clear(); - } - - @Override - public int mediaOutline(String app, String streamId) { - return gbStreamMapper.setStatus(app, streamId, false); - } - - @Override - public void updateParentPlatformStatus(String platformGbID, boolean online) { - platformMapper.updateParentPlatformStatus(platformGbID, online); - } - - @Override - public void updateMediaServer(MediaServerItem mediaServerItem) { - String now = this.format.format(System.currentTimeMillis()); - mediaServerItem.setUpdateTime(now); - if (mediaServerMapper.queryOne(mediaServerItem.getId()) != null) { - mediaServerMapper.update(mediaServerItem); - }else { - mediaServerItem.setCreateTime(now); - mediaServerMapper.add(mediaServerItem); - } - } - - @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); - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/Coordtransform.java b/src/main/java/com/genersoft/iot/vmp/utils/Coordtransform.java new file mode 100644 index 00000000..c10357c5 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/Coordtransform.java @@ -0,0 +1,126 @@ +package com.genersoft.iot.vmp.utils; + +/** + * 坐标转换 + * 一个提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换的工具类 + * 参考https://github.com/wandergis/coordtransform 写的Java版本 + * @author Xinconan + * @date 2016-03-18 + * @url https://github.com/xinconan/coordtransform + */ +public class Coordtransform { + + private static double x_PI = 3.14159265358979324 * 3000.0 / 180.0; + private static double PI = 3.1415926535897932384626; + private static double a = 6378245.0; + private static double ee = 0.00669342162296594323; + + /** + * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换 + * 即 百度 转 谷歌、高德 + * @param bd_lon + * @param bd_lat + * @return Double[lon,lat] + */ + public static Double[] BD09ToGCJ02(Double bd_lon,Double bd_lat){ + double x = bd_lon - 0.0065; + double y = bd_lat - 0.006; + double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI); + double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI); + Double[] arr = new Double[2]; + arr[0] = z * Math.cos(theta); + arr[1] = z * Math.sin(theta); + return arr; + } + + /** + * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换 + * 即谷歌、高德 转 百度 + * @param gcj_lon + * @param gcj_lat + * @return Double[lon,lat] + */ + public static Double[] GCJ02ToBD09(Double gcj_lon,Double gcj_lat){ + double z = Math.sqrt(gcj_lon * gcj_lon + gcj_lat * gcj_lat) + 0.00002 * Math.sin(gcj_lat * x_PI); + double theta = Math.atan2(gcj_lat, gcj_lon) + 0.000003 * Math.cos(gcj_lon * x_PI); + Double[] arr = new Double[2]; + arr[0] = z * Math.cos(theta) + 0.0065; + arr[1] = z * Math.sin(theta) + 0.006; + return arr; + } + + /** + * WGS84转GCJ02 + * @param wgs_lon + * @param wgs_lat + * @return Double[lon,lat] + */ + public static Double[] WGS84ToGCJ02(Double wgs_lon,Double wgs_lat){ + if(outOfChina(wgs_lon, wgs_lat)){ + return new Double[]{wgs_lon,wgs_lat}; + } + double dlat = transformlat(wgs_lon - 105.0, wgs_lat - 35.0); + double dlng = transformlng(wgs_lon - 105.0, wgs_lat - 35.0); + double radlat = wgs_lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + Double[] arr = new Double[2]; + arr[0] = wgs_lon + dlng; + arr[1] = wgs_lat + dlat; + return arr; + } + + /** + * GCJ02转WGS84 + * @param gcj_lon + * @param gcj_lat + * @return Double[lon,lat] + */ + public static Double[] GCJ02ToWGS84(Double gcj_lon,Double gcj_lat){ + if(outOfChina(gcj_lon, gcj_lat)){ + return new Double[]{gcj_lon,gcj_lat}; + } + double dlat = transformlat(gcj_lon - 105.0, gcj_lat - 35.0); + double dlng = transformlng(gcj_lon - 105.0, gcj_lat - 35.0); + double radlat = gcj_lat / 180.0 * PI; + double magic = Math.sin(radlat); + magic = 1 - ee * magic * magic; + double sqrtmagic = Math.sqrt(magic); + dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); + dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); + double mglat = gcj_lat + dlat; + double mglng = gcj_lon + dlng; + return new Double[]{gcj_lon * 2 - mglng, gcj_lat * 2 - mglat}; + } + + private static Double transformlat(double lng, double lat) { + double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0; + ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0; + return ret; + } + + private static Double transformlng(double lng,double lat) { + double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng)); + ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0; + ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0; + ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0; + return ret; + } + + /** + * outOfChina + * @描述: 判断是否在国内,不在国内则不做偏移 + * @param lng + * @param lat + * @return {boolean} + */ + private static boolean outOfChina(Double lng,Double lat) { + return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false); + }; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java new file mode 100644 index 00000000..f7e07293 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java @@ -0,0 +1,90 @@ +package com.genersoft.iot.vmp.utils; + + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; + +import java.util.Locale; + +/** + * 全局时间工具类 + * @author lin + */ +public class DateUtil { + + /** + * 兼容不规范的iso8601时间格式 + */ + private static final String ISO8601_COMPATIBLE_PATTERN = "yyyy-M-d'T'H:m:s"; + + /** + * 用以输出标准的iso8601时间格式 + */ + private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; + + /** + * wvp内部统一时间格式 + */ + public static final String PATTERN = "yyyy-MM-dd HH:mm:ss"; + + public static final String zoneStr = "Asia/Shanghai"; + + public static final DateTimeFormatter formatterCompatibleISO8601 = DateTimeFormatter.ofPattern(ISO8601_COMPATIBLE_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + public static final DateTimeFormatter formatterISO8601 = DateTimeFormatter.ofPattern(ISO8601_PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN, Locale.getDefault()).withZone(ZoneId.of(zoneStr)); + + public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) { + + return formatterISO8601.format(formatter.parse(formatTime)); + } + + public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) { + return formatter.format(formatterCompatibleISO8601.parse(formatTime)); + + } + + /** + * yyyy_MM_dd_HH_mm_ss 转时间戳 + * @param formatTime + * @return + */ + public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) { + TemporalAccessor temporalAccessor = formatter.parse(formatTime); + Instant instant = Instant.from(temporalAccessor); + return instant.getEpochSecond(); + } + + /** + * 获取当前时间 + * @return + */ + public static String getNow() { + LocalDateTime nowDateTime = LocalDateTime.now(); + return formatter.format(nowDateTime); + } + + /** + * 格式校验 + * @param timeStr 时间字符串 + * @param dateTimeFormatter 待校验的格式 + * @return + */ + public static boolean verification(String timeStr, DateTimeFormatter dateTimeFormatter) { + try { + LocalDate.parse(timeStr, dateTimeFormatter); + return true; + }catch (DateTimeParseException exception) { + return false; + } + } + + public static String getNowForISO8601() { + LocalDateTime nowDateTime = LocalDateTime.now(); + return formatterISO8601.format(nowDateTime); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/GitUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/GitUtil.java index 60ee987c..ca637dda 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/GitUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/GitUtil.java @@ -11,17 +11,24 @@ import org.springframework.stereotype.Component; @PropertySource(value = {"classpath:git.properties" }, ignoreResourceNotFound = true) public class GitUtil { - @Value("${git.branch:null}") + @Value("${git.branch:}") private String branch; - @Value("${git.commit.id:null}") + @Value("${git.commit.id:}") private String gitCommitId; - @Value("${git.remote.origin.url:null}") + @Value("${git.remote.origin.url:}") private String gitUrl; - @Value("${git.build.time:null}") + @Value("${git.build.time:}") private String buildDate; - @Value("${git.commit.id.abbrev:null}") + + @Value("${git.build.version:}") + private String buildVersion; + + @Value("${git.commit.id.abbrev:}") private String commitIdShort; + @Value("${git.commit.time:}") + private String commitTime; + public String getGitCommitId() { return gitCommitId; } @@ -41,4 +48,12 @@ public class GitUtil { public String getCommitIdShort() { return commitIdShort; } + + public String getBuildVersion() { + return buildVersion; + } + + public String getCommitTime() { + return commitTime; + } } 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 53e6a61d..2d9bd39b 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/GpsUtil.java @@ -17,48 +17,14 @@ public class GpsUtil { public static BaiduPoint Wgs84ToBd09(String xx, String yy) { - try { - Socket s = new Socket("api.map.baidu.com", 80); - BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8")); - OutputStream out = s.getOutputStream(); - StringBuffer sb = new StringBuffer("GET /ag/coord/convert?from=0&to=4"); - sb.append("&x=" + xx + "&y=" + yy); - sb.append("&callback=BMap.Convertor.cbk_3976 HTTP/1.1\r\n"); - sb.append("User-Agent: Java/1.6.0_20\r\n"); - sb.append("Host: api.map.baidu.com:80\r\n"); - sb.append("Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\r\n"); - sb.append("Connection: Close\r\n"); - sb.append("\r\n"); - out.write(sb.toString().getBytes()); - String json = ""; - String tmp = ""; - while ((tmp = br.readLine()) != null) { - // logger.info(tmp); - json += tmp; - } - - s.close(); - int start = json.indexOf("cbk_3976"); - int end = json.lastIndexOf("}"); - if (start != -1 && end != -1 && json.contains("\"x\":\"")) { - json = json.substring(start, end); - String[] point = json.split(","); - String x = point[1].split(":")[1].replace("\"", ""); - String y = point[2].split(":")[1].replace("\"", ""); - BaiduPoint bdPoint= new BaiduPoint(); - bdPoint.setBdLng(new String(decode(x))); - bdPoint.setBdLat(new String(decode(y))); - return bdPoint; - //return (new String(decode(x)) + "," + new String(decode(y))); - } else { - logger.info("gps坐标无效!!"); - } - out.close(); - br.close(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; + double lng = Double.parseDouble(xx); + double lat = Double.parseDouble(yy); + Double[] gcj02 = Coordtransform.WGS84ToGCJ02(lng, lat); + Double[] doubles = Coordtransform.GCJ02ToBD09(gcj02[0], gcj02[1]); + BaiduPoint bdPoint= new BaiduPoint(); + bdPoint.setBdLng(doubles[0] + ""); + bdPoint.setBdLat(doubles[1] + ""); + return bdPoint; } /** diff --git a/src/main/java/com/genersoft/iot/vmp/utils/IpUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/IpUtil.java deleted file mode 100644 index d754554b..00000000 --- a/src/main/java/com/genersoft/iot/vmp/utils/IpUtil.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.genersoft.iot.vmp.utils; - - -import javax.servlet.http.HttpServletRequest; -import java.net.InetAddress; -import java.net.UnknownHostException; - -public class IpUtil { - public static String getIpAddr(HttpServletRequest request) { - String ipAddress = null; - try { - ipAddress = request.getHeader("x-forwarded-for"); - if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { - ipAddress = request.getHeader("Proxy-Client-IP"); - } - if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { - ipAddress = request.getHeader("WL-Proxy-Client-IP"); - } - if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { - ipAddress = request.getRemoteAddr(); - if (ipAddress.equals("127.0.0.1")) { - // 根据网卡取本机配置的IP - InetAddress inet = null; - try { - inet = InetAddress.getLocalHost(); - } catch (UnknownHostException e) { - e.printStackTrace(); - } - ipAddress = inet.getHostAddress(); - } - } - // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 - if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length() - // = 15 - if (ipAddress.indexOf(",") > 0) { - ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); - } - } - } catch (Exception e) { - ipAddress=""; - } - // ipAddress = this.getRequest().getRemoteAddr(); - - return ipAddress; - } -} - - diff --git a/src/main/java/com/genersoft/iot/vmp/utils/JarFileUtils.java b/src/main/java/com/genersoft/iot/vmp/utils/JarFileUtils.java deleted file mode 100644 index 686b562d..00000000 --- a/src/main/java/com/genersoft/iot/vmp/utils/JarFileUtils.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.genersoft.iot.vmp.utils; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import org.springframework.util.ClassUtils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -/** - * 一个优秀的颓废程序猿 - */ -@Component -public class JarFileUtils { - private static Logger log = LoggerFactory.getLogger(JarFileUtils.class); - private static Map map = new HashMap<>(); - - public Map readJarFile() { - JarFile jarFile = null; - BufferedReader br = null; - try { - // 获取jar的运行路径,因linux下jar的路径为”file:/app/.../test.jar!/BOOT-INF/class!/“这种格式,所以需要去掉”file:“和”!/BOOT-INF/class!/“ - String jarFilePath = ClassUtils.getDefaultClassLoader().getResource("").getPath().replace("!/BOOT-INF/classes!/", ""); - if (jarFilePath.startsWith("file")) { - jarFilePath = jarFilePath.substring(5); - } - log.debug("jarFilePath:" + jarFilePath); - // 通过JarFile的getJarEntry方法读取META-INF/MANIFEST.MF - jarFile = new JarFile(jarFilePath); - JarEntry entry = jarFile.getJarEntry("META-INF/MANIFEST.MF"); - log.info("读取的内容:" + entry.toString()); - // 如果读取到MANIFEST.MF文件内容,则转换为string - if (entry != null) { - InputStream in = jarFile.getInputStream(entry); - - StringBuilder sb = new StringBuilder(); - br = new BufferedReader(new InputStreamReader(in)); - String line = ""; - while ((line = br.readLine()) != null) { - if (line != null && line.contains(":")) { - int index = line.indexOf(":"); - map.put(line.substring(0, index).trim(), line.substring(index + 1, line.length()).trim()); - } - } - return map; - } - } catch (IOException e) { - log.debug("读取MANIFEST.MF文件异常:" + e.getMessage()); - } finally { - try { - if (null != br) { - br.close(); - } - if (null != jarFile) { - jarFile.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - return map; - - } - -} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java b/src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java deleted file mode 100644 index ae91ad59..00000000 --- a/src/main/java/com/genersoft/iot/vmp/utils/SerializeUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.genersoft.iot.vmp.utils; - -import java.io.*; - -public class SerializeUtils { - public static byte[] serialize(Object obj){ - byte[] bytes = null; - try { - ByteArrayOutputStream baos=new ByteArrayOutputStream();; - ObjectOutputStream oos=new ObjectOutputStream(baos); - oos.writeObject(obj); - bytes=baos.toByteArray(); - baos.close(); - oos.close(); - } catch (IOException e) { - e.printStackTrace(); - } - return bytes; - } - public static Object deSerialize(byte[] bytes){ - Object obj=null; - try { - ByteArrayInputStream bais=new ByteArrayInputStream(bytes); - ObjectInputStream ois=new ObjectInputStream(bais); - obj=ois.readObject(); - } catch (Exception e) { - e.printStackTrace(); - } - return obj; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/SpringBeanFactory.java b/src/main/java/com/genersoft/iot/vmp/utils/SpringBeanFactory.java index 3d2b2ba0..53238754 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/SpringBeanFactory.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/SpringBeanFactory.java @@ -33,9 +33,11 @@ public class SpringBeanFactory implements ApplicationContextAware { /** * 获取对象 这里重写了bean方法,起主要作用 */ - public static Object getBean(String beanId) throws BeansException { - if (applicationContext == null) return null; - return applicationContext.getBean(beanId); + public static T getBean(String beanId) throws BeansException { + if (applicationContext == null) { + return null; + } + return (T) applicationContext.getBean(beanId); } /** diff --git a/src/main/java/com/genersoft/iot/vmp/utils/SystemInfoUtils.java b/src/main/java/com/genersoft/iot/vmp/utils/SystemInfoUtils.java new file mode 100644 index 00000000..ec2f3b22 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/utils/SystemInfoUtils.java @@ -0,0 +1,148 @@ +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 oshi.SystemInfo; +import oshi.hardware.*; +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; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * 实现参考自xiaozhangnomoney原创文章, + * 版权声明:本文为xiaozhangnomoney原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明 + * 原文出处链接:https://blog.csdn.net/xiaozhangnomoney/article/details/107769147 + */ +public class SystemInfoUtils { + + private final static Logger logger = LoggerFactory.getLogger(SystemInfoUtils.class); + + /** + * 获取cpu信息 + * @return + * @throws InterruptedException + */ + public static double getCpuInfo() throws InterruptedException { + SystemInfo systemInfo = new SystemInfo(); + CentralProcessor processor = systemInfo.getHardware().getProcessor(); + long[] prevTicks = processor.getSystemCpuLoadTicks(); + // 睡眠1s + TimeUnit.SECONDS.sleep(1); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()]; + long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()]; + long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()]; + long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()]; + long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()]; + long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()]; + long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()]; + long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + return 1.0-(idle * 1.0 / totalCpu); + } + + /** + * 获取内存使用率 + * @return + */ + public static double getMemInfo(){ + SystemInfo systemInfo = new SystemInfo(); + GlobalMemory memory = systemInfo.getHardware().getMemory(); + //总内存 + long totalByte = memory.getTotal(); + //剩余 + long acaliableByte = memory.getAvailable(); + return (totalByte-acaliableByte)*1.0/totalByte; + } + + /** + * 获取网络上传和下载 + * @return + */ + public static Map getNetworkInterfaces() { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + List beforeRecvNetworkIFs = hal.getNetworkIFs(); + NetworkIF beforeBet= beforeRecvNetworkIFs.get(beforeRecvNetworkIFs.size() - 1); + long beforeRecv = beforeBet.getBytesRecv(); + long beforeSend = beforeBet.getBytesSent(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error("[线程休眠失败] : {}", e.getMessage()); + } + List afterNetworkIFs = hal.getNetworkIFs(); + NetworkIF afterNet = afterNetworkIFs.get(afterNetworkIFs.size() - 1); + + HashMap map = new HashMap<>(); + // 速度单位: Mbps + map.put("in",formatUnits(afterNet.getBytesRecv()-beforeRecv, 1048576L)); + map.put("out",formatUnits(afterNet.getBytesSent()-beforeSend, 1048576L)); + return map; + } + + /** + * 获取带宽总值 + * @return + */ + public static long getNetworkTotal() { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + List recvNetworkIFs = hal.getNetworkIFs(); + NetworkIF networkIF= recvNetworkIFs.get(recvNetworkIFs.size() - 1); + + return networkIF.getSpeed()/1048576L/8L; + } + + public static double formatUnits(long value, long prefix) { + return (double)value / (double)prefix; + } + + /** + * 获取进程数 + * @return + */ + public static int getProcessesCount(){ + SystemInfo si = new SystemInfo(); + OperatingSystem os = si.getOperatingSystem(); + + int processCount = os.getProcessCount(); + return processCount; + } + + public static List> getDiskInfo() { + List> result = new ArrayList<>(); + + String osName = System.getProperty("os.name"); + List pathArray = new ArrayList<>(); + if (osName.startsWith("Mac OS")) { + // 苹果 + pathArray.add("/"); + } else if (osName.startsWith("Windows")) { + // windows + pathArray.add("C:"); + } else { + pathArray.add("/"); + pathArray.add("/home"); + } + for (String path : pathArray) { + Map infoMap = new HashMap<>(); + infoMap.put("path", path); + File partitionFile = new File(path); + // 单位: GB + infoMap.put("use", (partitionFile.getTotalSpace() - partitionFile.getFreeSpace())/1024/1024/1024D); + infoMap.put("free", partitionFile.getFreeSpace()/1024/1024/1024D); + result.add(infoMap); + } + return result; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/FastJsonRedisSerializer.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/FastJsonRedisSerializer.java index 05d7f811..466a5035 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/redis/FastJsonRedisSerializer.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/redis/FastJsonRedisSerializer.java @@ -1,12 +1,12 @@ package com.genersoft.iot.vmp.utils.redis; -import java.nio.charset.Charset; - +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.serializer.SerializerFeature; +import java.nio.charset.Charset; /** * @description:使用fastjson实现redis的序列化 @@ -29,7 +29,7 @@ public class FastJsonRedisSerializer implements RedisSerializer { if (t == null) { return new byte[0]; } - return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName, JSONWriter.Feature.WritePairAsJavaBean).getBytes(DEFAULT_CHARSET); } @Override @@ -38,6 +38,8 @@ public class FastJsonRedisSerializer implements RedisSerializer { return null; } String str = new String(bytes, DEFAULT_CHARSET); - return (T) JSON.parseObject(str, clazz); + return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); } + + } diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java deleted file mode 100644 index 1fb1d17e..00000000 --- a/src/main/java/com/genersoft/iot/vmp/utils/redis/JedisUtil.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.genersoft.iot.vmp.utils.redis; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; - -import java.util.Set; - -/** - * @description:Jedis工具类 - * @author: wangshaopeng@sunnybs.com - * @date: 2021年03月22日 下午8:27:29 - */ -@Component -public class JedisUtil { - - @Autowired - private JedisPool jedisPool; - - // ============================== Key ============================== - - /** - * 检查给定 key 是否存在。 - * - * @param key - * @return - */ - public Boolean exists(String key) { - Jedis jedis = null; - try { - jedis = jedisPool.getResource(); - Boolean exists = jedis.exists(key); - return exists; - } finally { - returnToPool(jedis); - } - } - - - // ============================== Set ============================== - - /** - * SADD key member [member ...] - * 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。 - * 假如 key 不存在,则创建一个只包含 member 元素作成员的集合。 - * 当 key 不是集合类型时,返回一个错误。 - */ - public Long sadd(String key, String... members) { - Jedis jedis = null; - try { - jedis = jedisPool.getResource(); - Long smove = jedis.sadd(key, members); - return smove; - } finally { - returnToPool(jedis); - } - } - - /** - * SMEMBERS key - * 返回集合 key 中的所有成员。 - * 不存在的 key 被视为空集合。 - */ - public Set smembers(String key) { - Jedis jedis = null; - try { - jedis = jedisPool.getResource(); - Set smembers = jedis.smembers(key); - return smembers; - } finally { - returnToPool(jedis); - } - } - - - /** - * SREM key member1 [member2] - * 移除集合中一个或多个成员 - */ - public Long srem(String key, String... member) { - Jedis jedis = null; - try { - jedis = jedisPool.getResource(); - Long srem = jedis.srem(key, member); - return srem; - } finally { - returnToPool(jedis); - } - } - - private void returnToPool(Jedis jedis) { - if (jedis != null) { - jedis.close(); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java index da098513..50152cda 100644 --- a/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java @@ -1,33 +1,37 @@ package com.genersoft.iot.vmp.utils.redis; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.utils.SpringBeanFactory; +import org.springframework.data.redis.core.*; +import org.springframework.util.CollectionUtils; + import java.util.*; import java.util.concurrent.TimeUnit; -import com.alibaba.fastjson.JSONObject; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.*; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - /** - * @description:Redis工具类 - * @author: swwheihei - * @date: 2020年5月6日 下午8:27:29 + * Redis工具类 + * @author swwheihei + * @date 2020年5月6日 下午8:27:29 */ -@Component @SuppressWarnings(value = {"rawtypes", "unchecked"}) public class RedisUtil { - @Autowired - private RedisTemplate redisTemplate; - + private static RedisTemplate redisTemplate; + + static { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } + /** * 指定缓存失效时间 * @param key 键 * @param time 时间(秒) * @return true / false */ - public boolean expire(String key, long time) { + public static boolean expire(String key, long time) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { if (time > 0) { redisTemplate.expire(key, time, TimeUnit.SECONDS); @@ -42,9 +46,11 @@ public class RedisUtil { /** * 根据 key 获取过期时间 * @param key 键 - * @return */ - public long getExpire(String key) { + public static long getExpire(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.getExpire(key, TimeUnit.SECONDS); } @@ -53,7 +59,10 @@ public class RedisUtil { * @param key 键 * @return true / false */ - public boolean hasKey(String key) { + public static boolean hasKey(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.hasKey(key); } catch (Exception e) { @@ -67,7 +76,10 @@ public class RedisUtil { * @SuppressWarnings("unchecked") 忽略类型转换警告 * @param key 键(一个或者多个) */ - public boolean del(String... key) { + public static boolean del(String... key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { if (key != null && key.length > 0) { if (key.length == 1) { @@ -91,7 +103,10 @@ public class RedisUtil { * @param key 键 * @return 值 */ - public Object get(String key) { + public static Object get(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return key == null ? null : redisTemplate.opsForValue().get(key); } @@ -101,7 +116,10 @@ public class RedisUtil { * @param value 值 * @return true / false */ - public boolean set(String key, Object value) { + public static boolean set(String key, Object value) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForValue().set(key, value); return true; @@ -118,7 +136,10 @@ public class RedisUtil { * @param time 时间(秒),如果 time < 0 则设置无限时间 * @return true / false */ - public boolean set(String key, Object value, long time) { + public static boolean set(String key, Object value, long time) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); @@ -138,7 +159,10 @@ public class RedisUtil { * @param delta 递增大小 * @return */ - public long incr(String key, long delta) { + public static long incr(String key, long delta) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } if (delta < 0) { throw new RuntimeException("递增因子必须大于 0"); } @@ -151,7 +175,10 @@ public class RedisUtil { * @param delta 递减大小 * @return */ - public long decr(String key, long delta) { + public static long decr(String key, long delta) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } if (delta < 0) { throw new RuntimeException("递减因子必须大于 0"); } @@ -166,7 +193,10 @@ public class RedisUtil { * @param item 项(no null) * @return 值 */ - public Object hget(String key, String item) { + public static Object hget(String key, String item) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForHash().get(key, item); } @@ -175,7 +205,10 @@ public class RedisUtil { * @param key 键(no null) * @return 对应的多个键值 */ - public Map hmget(String key) { + public static Map hmget(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForHash().entries(key); } @@ -185,7 +218,10 @@ public class RedisUtil { * @param map 值 * @return true / false */ - public boolean hmset(String key, Map map) { + public static boolean hmset(String key, Map map) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForHash().putAll(key, map); return true; @@ -202,7 +238,10 @@ public class RedisUtil { * @param time 时间 * @return true / false */ - public boolean hmset(String key, Map map, long time) { + public static boolean hmset(String key, Map map, long time) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForHash().putAll(key, map); if (time > 0) { @@ -222,7 +261,10 @@ public class RedisUtil { * @param value 值 * @return true / false */ - public boolean hset(String key, String item, Object value) { + public static boolean hset(String key, String item, Object value) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForHash().put(key, item, value); return true; @@ -240,7 +282,10 @@ public class RedisUtil { * @param time 时间(如果原来的 Hash表 设置了时间,这里会覆盖) * @return true / false */ - public boolean hset(String key, String item, Object value, long time) { + public static boolean hset(String key, String item, Object value, long time) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForHash().put(key, item, value); if (time > 0) { @@ -258,7 +303,10 @@ public class RedisUtil { * @param key 键 * @param item 项(可以多个,no null) */ - public void hdel(String key, Object... item) { + public static void hdel(String key, Object... item) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } redisTemplate.opsForHash().delete(key, item); } @@ -268,7 +316,10 @@ public class RedisUtil { * @param item 值(no null) * @return true / false */ - public boolean hHasKey(String key, String item) { + public static boolean hHasKey(String key, String item) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForHash().hasKey(key, item); } @@ -279,7 +330,10 @@ public class RedisUtil { * @param by 递增大小 > 0 * @return */ - public Double hincr(String key, String item, Double by) { + public static Double hincr(String key, String item, Double by) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForHash().increment(key, item, by); } @@ -290,7 +344,10 @@ public class RedisUtil { * @param by 递减大小 * @return */ - public Double hdecr(String key, String item, Double by) { + public static Double hdecr(String key, String item, Double by) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForHash().increment(key, item, -by); } @@ -301,7 +358,10 @@ public class RedisUtil { * @param key 键 * @return 值 */ - public Set sGet(String key) { + public static Set sGet(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.opsForSet().members(key); } catch (Exception e) { @@ -316,7 +376,10 @@ public class RedisUtil { * @param value 值 * @return true / false */ - public boolean sHasKey(String key, Object value) { + public static boolean sHasKey(String key, Object value) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.opsForSet().isMember(key, value); } catch (Exception e) { @@ -331,7 +394,10 @@ public class RedisUtil { * @param values 值(可以多个) * @return 成功个数 */ - public long sSet(String key, Object... values) { + public static long sSet(String key, Object... values) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.opsForSet().add(key, values); } catch (Exception e) { @@ -347,7 +413,10 @@ public class RedisUtil { * @param values 值(可以多个) * @return 成功放入个数 */ - public long sSet(String key, long time, Object... values) { + public static long sSet(String key, long time, Object... values) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { long count = redisTemplate.opsForSet().add(key, values); if (time > 0) { @@ -365,7 +434,10 @@ public class RedisUtil { * @param key 键 * @return 长度 */ - public long sGetSetSize(String key) { + public static long sGetSetSize(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.opsForSet().size(key); } catch (Exception e) { @@ -380,7 +452,10 @@ public class RedisUtil { * @param values 值 * @return 成功移除个数 */ - public long setRemove(String key, Object... values) { + public static long setRemove(String key, Object... values) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.opsForSet().remove(key, values); } catch (Exception e) { @@ -397,7 +472,10 @@ public class RedisUtil { * @param value * @param score */ - public void zAdd(Object key, Object value, double score) { + public static void zAdd(Object key, Object value, double score) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } redisTemplate.opsForZSet().add(key, value, score); } @@ -407,7 +485,10 @@ public class RedisUtil { * @param key * @param value */ - public void zRemove(Object key, Object value) { + public static void zRemove(Object key, Object value) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } redisTemplate.opsForZSet().remove(key, value); } @@ -418,7 +499,10 @@ public class RedisUtil { * @param value * @param delta -1 表示减 1 表示加1 */ - public Double zIncrScore(Object key, Object value, double delta) { + public static Double zIncrScore(Object key, Object value, double delta) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForZSet().incrementScore(key, value, delta); } @@ -429,7 +513,10 @@ public class RedisUtil { * @param value * @return */ - public Double zScore(Object key, Object value) { + public static Double zScore(Object key, Object value) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForZSet().score(key, value); } @@ -440,7 +527,10 @@ public class RedisUtil { * @param value * @return */ - public Long zRank(Object key, Object value) { + public static Long zRank(Object key, Object value) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForZSet().rank(key, value); } @@ -450,7 +540,10 @@ public class RedisUtil { * @param key * @return */ - public Long zSize(Object key) { + public static Long zSize(Object key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForZSet().zCard(key); } @@ -464,7 +557,10 @@ public class RedisUtil { * @param end * @return */ - public Set ZRange(Object key, int start, int end) { + public static Set zRange(Object key, int start, int end) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForZSet().range(key, start, end); } /** @@ -475,7 +571,10 @@ public class RedisUtil { * @param end * @return */ - public Set> zRangeWithScore(Object key, int start, int end) { + public static Set> zRangeWithScore(Object key, int start, int end) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForZSet().rangeWithScores(key, start, end); } /** @@ -488,7 +587,10 @@ public class RedisUtil { * @param end * @return */ - public Set zRevRange(Object key, int start, int end) { + public static Set zRevRange(Object key, int start, int end) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForZSet().reverseRange(key, start, end); } /** @@ -499,7 +601,10 @@ public class RedisUtil { * @param max * @return */ - public Set zSortRange(Object key, int min, int max) { + public static Set zSortRange(Object key, int min, int max) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } return redisTemplate.opsForZSet().rangeByScore(key, min, max); } @@ -513,7 +618,10 @@ public class RedisUtil { * @param end 结束(0 到 -1 代表所有值) * @return */ - public List lGet(String key, long start, long end) { + public static List lGet(String key, long start, long end) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.opsForList().range(key, start, end); } catch (Exception e) { @@ -527,7 +635,10 @@ public class RedisUtil { * @param key 键 * @return 长度 */ - public long lGetListSize(String key) { + public static long lGetListSize(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.opsForList().size(key); } catch (Exception e) { @@ -544,7 +655,10 @@ public class RedisUtil { * 当 index < 0 时 {-1:表尾, -2:倒数第二个元素} * @return 值 */ - public Object lGetIndex(String key, long index) { + public static Object lGetIndex(String key, long index) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { @@ -559,7 +673,10 @@ public class RedisUtil { * @param value 值 * @return true / false */ - public boolean lSet(String key, Object value) { + public static boolean lSet(String key, Object value) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForList().rightPush(key, value); return true; @@ -576,7 +693,10 @@ public class RedisUtil { * @param time 时间 * @return true / false */ - public boolean lSet(String key, Object value, long time) { + public static boolean lSet(String key, Object value, long time) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForList().rightPush(key, value); if (time > 0) { @@ -595,7 +715,10 @@ public class RedisUtil { * @param values 值 * @return true / false */ - public boolean lSetList(String key, List values) { + public static boolean lSetList(String key, List values) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForList().rightPushAll(key, values); return true; @@ -612,7 +735,10 @@ public class RedisUtil { * @param time 时间 * @return true / false */ - public boolean lSetList(String key, List values, long time) { + public static boolean lSetList(String key, List values, long time) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForList().rightPushAll(key, values); if (time > 0) { @@ -632,7 +758,10 @@ public class RedisUtil { * @param value 值 * @return true / false */ - public boolean lUpdateIndex(String key, long index, Object value) { + public static boolean lUpdateIndex(String key, long index, Object value) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { redisTemplate.opsForList().set(key, index, value); return true; @@ -651,7 +780,10 @@ public class RedisUtil { * @param value * @return */ - public long lRemove(String key, long count, Object value) { + public static long lRemove(String key, long count, Object value) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { return redisTemplate.opsForList().remove(key, count, value); } catch (Exception e) { @@ -660,12 +792,39 @@ public class RedisUtil { } } + /** + * 在键为 key 的 list中移除第一个元素 + * @param key 键 + * @return + */ + public static Object lLeftPop(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } + return redisTemplate.opsForList().leftPop(key); + } + + /** + * 在键为 key 的 list中移除、最后一个元素 + * @param key 键 + * @return + */ + public static Object lrightPop(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } + return redisTemplate.opsForList().rightPop(key); + } + /** * 模糊查询 * @param key 键 * @return true / false */ - public List keys(String key) { + public static List keys(String key) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } try { Set set = redisTemplate.keys(key); return new ArrayList<>(set); @@ -681,7 +840,7 @@ public class RedisUtil { * @param query 查询参数 * @return */ -// public List scan(String query) { +// public static List scan(String query) { // List result = new ArrayList<>(); // try { // Cursor> cursor = redisTemplate.opsForHash().scan("field", @@ -705,36 +864,30 @@ public class RedisUtil { * @param query 查询参数 * @return */ - public List scan(String query) { - Set keys = (Set) redisTemplate.execute((RedisCallback>) connection -> { - Set keysTmp = new HashSet<>(); - Cursor cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(query).count(1000).build()); - while (cursor.hasNext()) { - keysTmp.add(new String(cursor.next())); + public static List scan(String query) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } + Set resultKeys = (Set) redisTemplate.execute((RedisCallback>) connection -> { + ScanOptions scanOptions = ScanOptions.scanOptions().match("*" + query + "*").count(1000).build(); + Cursor scan = connection.scan(scanOptions); + Set keys = new HashSet<>(); + while (scan.hasNext()) { + byte[] next = scan.next(); + keys.add(new String(next)); } - return keysTmp; + return keys; }); -// Set keys = (Set) redisTemplate.execute(new RedisCallback>(){ -// -// @Override -// public Set doInRedis(RedisConnection connection) throws DataAccessException { -// Set keysTmp = new HashSet<>(); -// Cursor cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(query).count(1000).build()); -// while (cursor.hasNext()) { -// keysTmp.add(new String(cursor.next())); -// } -// return keysTmp; -// } -// }); - return new ArrayList<>(keys); + return new ArrayList<>(resultKeys); } // ============================== 消息发送与订阅 ============================== - public void convertAndSend(String channel, JSONObject msg) { -// redisTemplate.convertAndSend(channel, msg); + public static void convertAndSend(String channel, JSONObject msg) { + if (redisTemplate == null) { + redisTemplate = SpringBeanFactory.getBean("redisTemplate"); + } redisTemplate.convertAndSend(channel, msg); - } } 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 new file mode 100644 index 00000000..23690e54 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/BaseTree.java @@ -0,0 +1,87 @@ +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/BatchGBStreamParam.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/BatchGBStreamParam.java new file mode 100644 index 00000000..00904889 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/BatchGBStreamParam.java @@ -0,0 +1,23 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +import com.genersoft.iot.vmp.gb28181.bean.GbStream; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +/** + * @author lin + */ +@Schema(description = "多个推流信息") +public class BatchGBStreamParam { + @Schema(description = "推流信息列表") + private List gbStreams; + + public List getGbStreams() { + return gbStreams; + } + + public void setGbStreams(List gbStreams) { + this.gbStreams = gbStreams; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeferredResultEx.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeferredResultEx.java new file mode 100644 index 00000000..0b9d3d9a --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeferredResultEx.java @@ -0,0 +1,31 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +import org.springframework.web.context.request.async.DeferredResult; + +public class DeferredResultEx { + + private DeferredResult deferredResult; + + private DeferredResultFilter filter; + + public DeferredResultEx(DeferredResult result) { + this.deferredResult = result; + } + + + public DeferredResult getDeferredResult() { + return deferredResult; + } + + public void setDeferredResult(DeferredResult deferredResult) { + this.deferredResult = deferredResult; + } + + public DeferredResultFilter getFilter() { + return filter; + } + + public void setFilter(DeferredResultFilter filter) { + this.filter = filter; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeferredResultFilter.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeferredResultFilter.java new file mode 100644 index 00000000..18c22407 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/DeferredResultFilter.java @@ -0,0 +1,6 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +public interface DeferredResultFilter { + + Object handler(Object o); +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ErrorCode.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ErrorCode.java new file mode 100644 index 00000000..7e2b5120 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ErrorCode.java @@ -0,0 +1,30 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +/** + * 全局错误码 + */ +public enum ErrorCode { + SUCCESS(0, "成功"), + ERROR100(100, "失败"), + ERROR400(400, "参数不全或者错误"), + ERROR404(404, "资源未找到"), + ERROR403(403, "无权限操作"), + ERROR401(401, "请登录后重新请求"), + ERROR500(500, "系统异常"); + + private final int code; + private final String msg; + + ErrorCode(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public int getCode() { + return code; + } + + public String getMsg() { + return msg; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/PlayTypeEnum.java similarity index 86% rename from src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java rename to src/main/java/com/genersoft/iot/vmp/vmanager/bean/PlayTypeEnum.java index 58fbb26f..d2ee56bf 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/session/PlayTypeEnum.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/PlayTypeEnum.java @@ -1,4 +1,4 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.session; +package com.genersoft.iot.vmp.vmanager.bean; public enum PlayTypeEnum { diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ResourceBaceInfo.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ResourceBaceInfo.java new file mode 100644 index 00000000..b50d97a0 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ResourceBaceInfo.java @@ -0,0 +1,22 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +public class ResourceBaceInfo { + private int total; + private int online; + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public int getOnline() { + return online; + } + + public void setOnline(int online) { + this.online = online; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ResourceInfo.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ResourceInfo.java new file mode 100644 index 00000000..b8d7009f --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/ResourceInfo.java @@ -0,0 +1,41 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +public class ResourceInfo { + + private ResourceBaceInfo device; + private ResourceBaceInfo channel; + private ResourceBaceInfo push; + private ResourceBaceInfo proxy; + + public ResourceBaceInfo getDevice() { + return device; + } + + public void setDevice(ResourceBaceInfo device) { + this.device = device; + } + + public ResourceBaceInfo getChannel() { + return channel; + } + + public void setChannel(ResourceBaceInfo channel) { + this.channel = channel; + } + + public ResourceBaceInfo getPush() { + return push; + } + + public void setPush(ResourceBaceInfo push) { + this.push = push; + } + + public ResourceBaceInfo getProxy() { + return proxy; + } + + public void setProxy(ResourceBaceInfo proxy) { + this.proxy = proxy; + } +} 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 new file mode 100644 index 00000000..e7c24aae --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamContent.java @@ -0,0 +1,358 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +import com.genersoft.iot.vmp.common.StreamInfo; + +public class StreamContent { + + private String app; + private String stream; + + private String ip; + + private String flv; + + private String https_flv; + private String ws_flv; + private String wss_flv; + private String fmp4; + private String https_fmp4; + private String ws_fmp4; + private String wss_fmp4; + private String hls; + private String https_hls; + private String ws_hls; + private String wss_hls; + private String ts; + private String https_ts; + private String ws_ts; + private String wss_ts; + private String rtmp; + private String rtmps; + private String rtsp; + private String rtsps; + private String rtc; + + private String rtcs; + private String mediaServerId; + private Object tracks; + + private String startTime; + + private String endTime; + + private double progress; + + public StreamContent(StreamInfo streamInfo) { + if (streamInfo == null) { + return; + } + this.app = streamInfo.getApp(); + this.stream = streamInfo.getStream(); + if (streamInfo.getFlv() != null) { + this.flv = streamInfo.getFlv().getUrl(); + } + if (streamInfo.getHttps_flv() != null) { + this.https_flv = streamInfo.getHttps_flv().getUrl(); + } + if (streamInfo.getWs_flv() != null) { + this.ws_flv = streamInfo.getWs_flv().getUrl(); + } + if (streamInfo.getWss_flv() != null) { + this.wss_flv = streamInfo.getWss_flv().getUrl(); + } + if (streamInfo.getFmp4() != null) { + this.fmp4 = streamInfo.getFmp4().getUrl(); + } + if (streamInfo.getWs_fmp4() != null) { + this.ws_fmp4 = streamInfo.getWs_fmp4().getUrl(); + } + if (streamInfo.getWss_fmp4() != null) { + this.wss_fmp4 = streamInfo.getWss_fmp4().getUrl(); + } + if (streamInfo.getHls() != null) { + this.hls = streamInfo.getHls().getUrl(); + } + if (streamInfo.getHttps_hls() != null) { + this.https_hls = streamInfo.getHttps_hls().getUrl(); + } + if (streamInfo.getWs_hls() != null) { + this.ws_hls = streamInfo.getWs_hls().getUrl(); + } + if (streamInfo.getWss_hls() != null) { + this.wss_hls = streamInfo.getWss_hls().getUrl(); + } + if (streamInfo.getTs() != null) { + this.ts = streamInfo.getTs().getUrl(); + } + if (streamInfo.getHttps_ts() != null) { + this.https_ts = streamInfo.getHttps_ts().getUrl(); + } + if (streamInfo.getWs_ts() != null) { + this.ws_ts = streamInfo.getWs_ts().getUrl(); + } + if (streamInfo.getRtmp() != null) { + this.rtmp = streamInfo.getRtmp().getUrl(); + } + if (streamInfo.getRtmps() != null) { + this.rtmps = streamInfo.getRtmps().getUrl(); + } + if (streamInfo.getRtsp() != null) { + this.rtsp = streamInfo.getRtsp().getUrl(); + } + if (streamInfo.getRtsps() != null) { + this.rtsps = streamInfo.getRtsps().getUrl(); + } + if (streamInfo.getRtc() != null) { + this.rtc = streamInfo.getRtc().getUrl(); + } + if (streamInfo.getRtcs() != null) { + this.rtcs = streamInfo.getRtcs().getUrl(); + } + + this.mediaServerId = streamInfo.getMediaServerId(); + this.tracks = streamInfo.getTracks(); + this.startTime = streamInfo.getStartTime(); + this.endTime = streamInfo.getEndTime(); + this.progress = streamInfo.getProgress(); + } + + 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 getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getFlv() { + return flv; + } + + public void setFlv(String flv) { + this.flv = flv; + } + + public String getHttps_flv() { + return https_flv; + } + + public void setHttps_flv(String https_flv) { + this.https_flv = https_flv; + } + + public String getWs_flv() { + return ws_flv; + } + + public void setWs_flv(String ws_flv) { + this.ws_flv = ws_flv; + } + + public String getWss_flv() { + return wss_flv; + } + + public void setWss_flv(String wss_flv) { + this.wss_flv = wss_flv; + } + + public String getFmp4() { + return fmp4; + } + + public void setFmp4(String fmp4) { + this.fmp4 = fmp4; + } + + public String getHttps_fmp4() { + return https_fmp4; + } + + public void setHttps_fmp4(String https_fmp4) { + this.https_fmp4 = https_fmp4; + } + + public String getWs_fmp4() { + return ws_fmp4; + } + + public void setWs_fmp4(String ws_fmp4) { + this.ws_fmp4 = ws_fmp4; + } + + public String getWss_fmp4() { + return wss_fmp4; + } + + public void setWss_fmp4(String wss_fmp4) { + this.wss_fmp4 = wss_fmp4; + } + + public String getHls() { + return hls; + } + + public void setHls(String hls) { + this.hls = hls; + } + + public String getHttps_hls() { + return https_hls; + } + + public void setHttps_hls(String https_hls) { + this.https_hls = https_hls; + } + + public String getWs_hls() { + return ws_hls; + } + + public void setWs_hls(String ws_hls) { + this.ws_hls = ws_hls; + } + + public String getWss_hls() { + return wss_hls; + } + + public void setWss_hls(String wss_hls) { + this.wss_hls = wss_hls; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getHttps_ts() { + return https_ts; + } + + public void setHttps_ts(String https_ts) { + this.https_ts = https_ts; + } + + public String getWs_ts() { + return ws_ts; + } + + public void setWs_ts(String ws_ts) { + this.ws_ts = ws_ts; + } + + public String getWss_ts() { + return wss_ts; + } + + public void setWss_ts(String wss_ts) { + this.wss_ts = wss_ts; + } + + public String getRtmp() { + return rtmp; + } + + public void setRtmp(String rtmp) { + this.rtmp = rtmp; + } + + public String getRtmps() { + return rtmps; + } + + public void setRtmps(String rtmps) { + this.rtmps = rtmps; + } + + public String getRtsp() { + return rtsp; + } + + public void setRtsp(String rtsp) { + this.rtsp = rtsp; + } + + public String getRtsps() { + return rtsps; + } + + public void setRtsps(String rtsps) { + this.rtsps = rtsps; + } + + public String getRtc() { + return rtc; + } + + public void setRtc(String rtc) { + this.rtc = rtc; + } + + public String getRtcs() { + return rtcs; + } + + public void setRtcs(String rtcs) { + this.rtcs = rtcs; + } + + public String getMediaServerId() { + return mediaServerId; + } + + public void setMediaServerId(String mediaServerId) { + this.mediaServerId = mediaServerId; + } + + public Object getTracks() { + return tracks; + } + + public void setTracks(Object tracks) { + this.tracks = tracks; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(String endTime) { + this.endTime = endTime; + } + + public double getProgress() { + return progress; + } + + public void setProgress(double progress) { + this.progress = progress; + } +} 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 new file mode 100644 index 00000000..dcec6076 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/StreamPushExcelDto.java @@ -0,0 +1,88 @@ +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/SystemConfigInfo.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/SystemConfigInfo.java new file mode 100644 index 00000000..f5a52b97 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/SystemConfigInfo.java @@ -0,0 +1,47 @@ +package com.genersoft.iot.vmp.vmanager.bean; + +import com.genersoft.iot.vmp.common.VersionPo; +import com.genersoft.iot.vmp.conf.SipConfig; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.VersionInfo; + +public class SystemConfigInfo { + + private int serverPort; + private SipConfig sip; + private UserSetting addOn; + private VersionPo version; + + public int getServerPort() { + return serverPort; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } + + public SipConfig getSip() { + return sip; + } + + public void setSip(SipConfig sip) { + this.sip = sip; + } + + public UserSetting getAddOn() { + return addOn; + } + + public void setAddOn(UserSetting addOn) { + this.addOn = addOn; + } + + public VersionPo getVersion() { + return version; + } + + public void setVersion(VersionPo version) { + this.version = version; + } +} + diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java index f8e2c1c0..6ad1ed7d 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java @@ -1,11 +1,45 @@ package com.genersoft.iot.vmp.vmanager.bean; -public class WVPResult { +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "统一返回结果") +public class WVPResult implements Cloneable{ + + public WVPResult() { + } + + public WVPResult(int code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + + @Schema(description = "错误码,0为成功") private int code; + @Schema(description = "描述,错误时描述错误原因") private String msg; + @Schema(description = "数据") private T data; + + public static WVPResult success(T t, String msg) { + return new WVPResult<>(ErrorCode.SUCCESS.getCode(), msg, t); + } + + public static WVPResult success(T t) { + return success(t, ErrorCode.SUCCESS.getMsg()); + } + + public static WVPResult fail(int code, String msg) { + return new WVPResult<>(code, msg, null); + } + + public static WVPResult fail(ErrorCode errorCode) { + return fail(errorCode.getCode(), errorCode.getMsg()); + } + public int getCode() { return code; } @@ -29,4 +63,9 @@ public class WVPResult { public void setData(T data) { this.data = data; } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java index 860f6a38..f399f300 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java @@ -1,22 +1,24 @@ package com.genersoft.iot.vmp.vmanager.gb28181.MobilePosition; +import java.text.ParseException; import java.util.List; import java.util.UUID; -import javax.sip.message.Response; - +import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; 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.IVideoManagerStorager; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.github.pagehelper.util.StringUtil; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +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; @@ -30,10 +32,13 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; + /** * 位置信息管理 */ -@Api(tags = "位置信息管理") +@Tag(name = "位置信息管理") @CrossOrigin @RestController @RequestMapping("/api/position") @@ -42,7 +47,7 @@ public class MobilePositionController { private final static Logger logger = LoggerFactory.getLogger(MobilePositionController.class); @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private SIPCommander cmder; @@ -50,26 +55,26 @@ public class MobilePositionController { @Autowired private DeferredResultHolder resultHolder; + @Autowired + private IDeviceService deviceService; + /** - * 查询历史轨迹 + * 查询历史轨迹 * @param deviceId 设备ID * @param start 开始时间 * @param end 结束时间 * @return */ - @ApiOperation("查询历史轨迹") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "start", value = "开始时间", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "end", value = "结束时间", required = true, dataTypeClass = String.class), - }) + @Operation(summary = "查询历史轨迹") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号") + @Parameter(name = "start", description = "开始时间") + @Parameter(name = "end", description = "结束时间") @GetMapping("/history/{deviceId}") - public ResponseEntity> positions(@PathVariable String deviceId, - @RequestParam(required = false) String start, - @RequestParam(required = false) String end) { -// if (logger.isDebugEnabled()) { -// logger.debug("查询设备" + deviceId + "的历史轨迹"); -// } + public List positions(@PathVariable String deviceId, + @RequestParam(required = false) String channelId, + @RequestParam(required = false) String start, + @RequestParam(required = false) String end) { if (StringUtil.isEmpty(start)) { start = null; @@ -77,9 +82,7 @@ public class MobilePositionController { if (StringUtil.isEmpty(end)) { end = null; } - - List result = storager.queryMobilePositions(deviceId, start, end); - return new ResponseEntity<>(result, HttpStatus.OK); + return storager.queryMobilePositions(deviceId, channelId, start, end); } /** @@ -87,17 +90,11 @@ public class MobilePositionController { * @param deviceId 设备ID * @return */ - @ApiOperation("查询设备最新位置") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataTypeClass = String.class), - }) + @Operation(summary = "查询设备最新位置") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/latest/{deviceId}") - public ResponseEntity latestPosition(@PathVariable String deviceId) { -// if (logger.isDebugEnabled()) { -// logger.debug("查询设备" + deviceId + "的最新位置"); -// } - MobilePosition result = storager.queryLatestPosition(deviceId); - return new ResponseEntity<>(result, HttpStatus.OK); + public MobilePosition latestPosition(@PathVariable String deviceId) { + return storager.queryLatestPosition(deviceId); } /** @@ -105,23 +102,26 @@ public class MobilePositionController { * @param deviceId 设备ID * @return */ - @ApiOperation("获取移动位置信息") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataTypeClass = String.class), - }) + @Operation(summary = "获取移动位置信息") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/realtime/{deviceId}") - public DeferredResult> realTimePosition(@PathVariable String deviceId) { + public DeferredResult realTimePosition(@PathVariable String deviceId) { Device device = storager.queryVideoDevice(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_MOBILEPOSITION + deviceId; - cmder.mobilePostitionQuery(device, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("获取移动位置信息失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(5*1000L); + try { + cmder.mobilePostitionQuery(device, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("获取移动位置信息失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 获取移动位置信息: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult result = new DeferredResult(5*1000L); result.onTimeout(()->{ logger.warn(String.format("获取移动位置信息超时")); // 释放rtpserver @@ -142,33 +142,24 @@ public class MobilePositionController { * @param interval 上报时间间隔 * @return true = 命令发送成功 */ - @ApiOperation("订阅位置信息") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "expires", value = "订阅超时时间", dataTypeClass = String.class), - @ApiImplicitParam(name = "interval", value = "上报时间间隔", dataTypeClass = String.class), - }) + @Operation(summary = "订阅位置信息") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "expires", description = "订阅超时时间", required = true) + @Parameter(name = "interval", description = "上报时间间隔", required = true) @GetMapping("/subscribe/{deviceId}") - public ResponseEntity positionSubscribe(@PathVariable String deviceId, + public void positionSubscribe(@PathVariable String deviceId, @RequestParam String expires, @RequestParam String interval) { - String msg = ((expires.equals("0")) ? "取消" : "") + "订阅设备" + deviceId + "的移动位置"; - if (logger.isDebugEnabled()) { - logger.debug(msg); - } if (StringUtil.isEmpty(interval)) { interval = "5"; } Device device = storager.queryVideoDevice(deviceId); - - String result = msg; - if (cmder.mobilePositionSubscribe(device, Integer.parseInt(expires), Integer.parseInt(interval))) { - result += ",成功"; - } else { - result += ",失败"; + device.setSubscribeCycleForMobilePosition(Integer.parseInt(expires)); + device.setMobilePositionSubmissionInterval(Integer.parseInt(interval)); + deviceService.updateDevice(device); + if (!deviceService.removeMobilePositionSubscribe(device)) { + throw new ControllerException(ErrorCode.ERROR100); } - - return new ResponseEntity<>(result, HttpStatus.OK); } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java index 5f098288..d58d4314 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java @@ -1,10 +1,8 @@ package com.genersoft.iot.vmp.vmanager.gb28181.SseController; import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; + +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.CrossOrigin; @@ -18,7 +16,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; * @author: lawrencehj * @data: 2021-01-20 */ -@Api(tags = "SSE推送") +@Tag(name = "SSE推送") @CrossOrigin @Controller @RequestMapping("/api") @@ -26,10 +24,6 @@ public class SseController { @Autowired AlarmEventListener alarmEventListener; - @ApiOperation("浏览器推送") - @ApiImplicitParams({ - @ApiImplicitParam(name = "browserId", value = "浏览器ID", dataTypeClass = String.class), - }) @GetMapping("/emit") public SseEmitter emit(@RequestParam String browserId) { final SseEmitter sseEmitter = new SseEmitter(0L); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java index 434bbd49..e030b176 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java @@ -1,37 +1,139 @@ package com.genersoft.iot.vmp.vmanager.gb28181.alarm; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +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.transmit.cmd.ISIPCommander; +import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookListener; import com.genersoft.iot.vmp.service.IDeviceAlarmService; -import com.genersoft.iot.vmp.service.IGbStreamService; +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.WVPResult; import com.github.pagehelper.PageInfo; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import io.swagger.models.auth.In; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +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.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; import java.text.ParseException; -import java.text.SimpleDateFormat; +import java.time.LocalDateTime; import java.util.Arrays; -import java.util.Date; import java.util.List; -@Api(tags = "报警信息管理") +@Tag(name = "报警信息管理") @CrossOrigin @RestController @RequestMapping("/api/alarm") public class AlarmController { + private final static Logger logger = LoggerFactory.getLogger(AlarmController.class); + @Autowired private IDeviceAlarmService deviceAlarmService; - private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + @Autowired + private ISIPCommander commander; + + @Autowired + private ISIPCommanderForPlatform commanderForPlatform; + + @Autowired + private IVideoManagerStorage storage; + + + /** + * 删除报警 + * + * @param id 报警id + * @param deviceIds 多个设备id,逗号分隔 + * @param time 结束时间(这个时间之前的报警会被删除) + * @return + */ + @DeleteMapping("/delete") + @Operation(summary = "删除报警") + @Parameter(name = "id", description = "ID") + @Parameter(name = "deviceIds", description = "多个设备id,逗号分隔") + @Parameter(name = "time", description = "结束时间") + public Integer delete( + @RequestParam(required = false) Integer id, + @RequestParam(required = false) String deviceIds, + @RequestParam(required = false) String time + ) { + if (ObjectUtils.isEmpty(id)) { + id = null; + } + if (ObjectUtils.isEmpty(deviceIds)) { + deviceIds = null; + } + if (ObjectUtils.isEmpty(time)) { + time = null; + } + if (!DateUtil.verification(time, DateUtil.formatter) ){ + return null; + } + List deviceIdList = null; + if (deviceIds != null) { + String[] deviceIdArray = deviceIds.split(","); + deviceIdList = Arrays.asList(deviceIdArray); + } + + return deviceAlarmService.clearAlarmBeforeTime(id, deviceIdList, time); + } + + /** + * 测试向上级/设备发送模拟报警通知 + * + * @param deviceId 报警id + * @return + */ + @GetMapping("/test/notify/alarm") + @Operation(summary = "测试向上级/设备发送模拟报警通知") + @Parameter(name = "deviceId", description = "设备国标编号") + public void delete(@RequestParam String deviceId) { + Device device = storage.queryVideoDevice(deviceId); + ParentPlatform platform = storage.queryParentPlatByServerGBId(deviceId); + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setChannelId(deviceId); + deviceAlarm.setAlarmDescription("test"); + deviceAlarm.setAlarmMethod("1"); + deviceAlarm.setAlarmPriority("1"); + deviceAlarm.setAlarmTime(DateUtil.formatterISO8601.format(LocalDateTime.now())); + deviceAlarm.setAlarmType("1"); + deviceAlarm.setLongitude(115.33333); + deviceAlarm.setLatitude(39.33333); + + if (device != null && platform == null) { + + try { + commander.sendAlarmMessage(device, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + + } + }else if (device == null && platform != null){ + try { + commanderForPlatform.sendAlarmMessage(platform, deviceAlarm); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + }else { + throw new ControllerException(ErrorCode.ERROR100.getCode(),"无法确定" + deviceId + "是平台还是设备"); + } + + } /** * 分页查询报警 @@ -46,92 +148,48 @@ public class AlarmController { * @param endTime 结束时间 * @return */ - @ApiOperation("分页查询报警") + @Operation(summary = "分页查询报警") + @Parameter(name = "page",description = "当前页",required = true) + @Parameter(name = "count",description = "每页查询数量",required = true) + @Parameter(name = "deviceId",description = "设备id") + @Parameter(name = "alarmPriority",description = "查询内容") + @Parameter(name = "alarmMethod",description = "查询内容") + @Parameter(name = "alarmType",description = "每页查询数量") + @Parameter(name = "startTime",description = "开始时间") + @Parameter(name = "endTime",description = "结束时间") @GetMapping("/all") - @ApiImplicitParams({ - @ApiImplicitParam(name="deviceId", value = "设备id", dataTypeClass = String.class), - @ApiImplicitParam(name="page", value = "当前页", required = true ,dataTypeClass = Integer.class), - @ApiImplicitParam(name="count", value = "每页查询数量", required = true ,dataTypeClass = Integer.class), - @ApiImplicitParam(name="alarmPriority", value = "查询内容" ,dataTypeClass = String.class), - @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class), - @ApiImplicitParam(name="alarmMethod", value = "查询内容" ,dataTypeClass = String.class), - @ApiImplicitParam(name="alarmType", value = "查询内容" ,dataTypeClass = String.class), - @ApiImplicitParam(name="startTime", value = "查询内容" ,dataTypeClass = String.class), - @ApiImplicitParam(name="endTime", value = "查询内容" ,dataTypeClass = String.class), - }) - public ResponseEntity> getAll( - @RequestParam int page, - @RequestParam int count, - @RequestParam(required = false) String deviceId, - @RequestParam(required = false) String alarmPriority, - @RequestParam(required = false) String alarmMethod, - @RequestParam(required = false) String alarmType, - @RequestParam(required = false) String startTime, - @RequestParam(required = false) String endTime - ) { - if (StringUtils.isEmpty(alarmPriority)) alarmPriority = null; - if (StringUtils.isEmpty(alarmMethod)) alarmMethod = null; - if (StringUtils.isEmpty(alarmType)) alarmType = null; - if (StringUtils.isEmpty(startTime)) startTime = null; - if (StringUtils.isEmpty(endTime)) endTime = null; - - - try { - if (startTime != null) format.parse(startTime); - if (endTime != null) format.parse(endTime); - } catch (ParseException e) { - return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); - } - - PageInfo allAlarm = deviceAlarmService.getAllAlarm(page, count, deviceId, alarmPriority, alarmMethod, - alarmType, startTime, endTime); - return new ResponseEntity<>(allAlarm, HttpStatus.OK); - } - - - /** - * 删除报警 - * - * @param id 报警id - * @param deviceIds 多个设备id,逗号分隔 - * @param time 结束时间(这个时间之前的报警会被删除) - * @return - */ - @ApiOperation("删除报警") - @DeleteMapping("/delete") - @ApiImplicitParams({ - @ApiImplicitParam(name="id", value = "ID", required = false ,dataTypeClass = Integer.class), - @ApiImplicitParam(name="deviceIds", value = "多个设备id,逗号分隔", required = false ,dataTypeClass = String.class), - @ApiImplicitParam(name="time", value = "结束时间", required = false ,dataTypeClass = String.class), - }) - public ResponseEntity> delete( - @RequestParam(required = false) Integer id, - @RequestParam(required = false) String deviceIds, - @RequestParam(required = false) String time + public PageInfo getAll( + @RequestParam int page, + @RequestParam int count, + @RequestParam(required = false) String deviceId, + @RequestParam(required = false) String alarmPriority, + @RequestParam(required = false) String alarmMethod, + @RequestParam(required = false) String alarmType, + @RequestParam(required = false) String startTime, + @RequestParam(required = false) String endTime ) { - if (StringUtils.isEmpty(id)) id = null; - if (StringUtils.isEmpty(deviceIds)) deviceIds = null; - if (StringUtils.isEmpty(time)) time = null; - try { - if (time != null) { - format.parse(time); - } - } catch (ParseException e) { - return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); + if (ObjectUtils.isEmpty(alarmPriority)) { + alarmPriority = null; } - List deviceIdList = null; - if (deviceIds != null) { - String[] deviceIdArray = deviceIds.split(","); - deviceIdList = Arrays.asList(deviceIdArray); + if (ObjectUtils.isEmpty(alarmMethod)) { + alarmMethod = null; + } + if (ObjectUtils.isEmpty(alarmType)) { + alarmType = null; + } + if (ObjectUtils.isEmpty(startTime)) { + startTime = null; + } + if (ObjectUtils.isEmpty(endTime)) { + endTime = null; } - int count = deviceAlarmService.clearAlarmBeforeTime(id, deviceIdList, time); - WVPResult wvpResult = new WVPResult(); - wvpResult.setCode(0); - wvpResult.setMsg("success"); - wvpResult.setData(count); - return new ResponseEntity>(wvpResult, HttpStatus.OK); + + if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间或结束时间格式有误"); + } + + return deviceAlarmService.getAllAlarm(page, count, deviceId, alarmPriority, alarmMethod, + alarmType, startTime, endTime); } - - } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java index b3de5af8..651d9ac5 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java @@ -7,30 +7,33 @@ package com.genersoft.iot.vmp.vmanager.gb28181.device; -import javax.sip.message.Response; - -import com.alibaba.fastjson.JSONObject; +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.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.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; import java.util.UUID; -@Api(tags = "国标设备配置") +@Tag(name = "国标设备配置") @CrossOrigin @RestController @RequestMapping("/api/device/config") @@ -39,7 +42,7 @@ public class DeviceConfig { private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private SIPCommander cmder; @@ -57,17 +60,15 @@ public class DeviceConfig { * @param heartBeatCount 心跳计数 * @return */ - @ApiOperation("基本配置设置命令") @GetMapping("/basicParam/{deviceId}") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value ="设备ID" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value ="通道ID",dataTypeClass = String.class ), - @ApiImplicitParam(name = "name", value ="名称" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "expiration", value ="到期时间" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "heartBeatInterval", value ="心跳间隔" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "heartBeatCount", value ="心跳计数" ,dataTypeClass = String.class), - }) - public DeferredResult> homePositionApi(@PathVariable String deviceId, + @Operation(summary = "基本配置设置命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "name", description = "名称") + @Parameter(name = "expiration", description = "到期时间") + @Parameter(name = "heartBeatInterval", description = "心跳间隔") + @Parameter(name = "heartBeatCount", description = "心跳计数") + public DeferredResult homePositionApi(@PathVariable String deviceId, String channelId, @RequestParam(required = false) String name, @RequestParam(required = false) String expiration, @@ -79,14 +80,19 @@ public class DeviceConfig { Device device = storager.queryVideoDevice(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONFIG + deviceId + channelId; - cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("设备配置操作失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(3 * 1000L); + try { + cmder.deviceBasicConfigCmd(device, channelId, name, expiration, heartBeatInterval, heartBeatCount, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("设备配置操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 设备配置: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult result = new DeferredResult(3 * 1000L); result.onTimeout(() -> { logger.warn(String.format("设备配置操作超时, 设备未返回应答指令")); // 释放rtpserver @@ -111,30 +117,33 @@ public class DeviceConfig { * @param channelId 通道ID * @return */ - @ApiOperation("设备配置查询请求") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value ="设备ID" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value ="通道ID" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "configType", value ="配置类型" ,dataTypeClass = String.class), - }) + @Operation(summary = "设备配置查询请求") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "configType", description = "配置类型") @GetMapping("/query/{deviceId}/{configType}") - public DeferredResult> configDownloadApi(@PathVariable String deviceId, + public DeferredResult configDownloadApi(@PathVariable String deviceId, @PathVariable String configType, @RequestParam(required = false) String channelId) { if (logger.isDebugEnabled()) { logger.debug("设备状态查询API调用"); } - String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (StringUtils.isEmpty(channelId) ? deviceId : channelId); + String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); String uuid = UUID.randomUUID().toString(); Device device = storager.queryVideoDevice(deviceId); - cmder.deviceConfigQuery(device, channelId, configType, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("获取设备配置失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult> (3 * 1000L); + try { + cmder.deviceConfigQuery(device, channelId, configType, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("获取设备配置失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 获取设备配置: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult result = new DeferredResult (3 * 1000L); result.onTimeout(()->{ logger.warn(String.format("获取设备配置超时")); // 释放rtpserver diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java index 20aa9576..18618e74 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java @@ -7,31 +7,31 @@ package com.genersoft.iot.vmp.vmanager.gb28181.device; -import javax.sip.message.Response; - -import com.alibaba.fastjson.JSONObject; +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.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.IVideoManagerStorager; - -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +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.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; import java.util.UUID; -@Api(tags = "国标设备控制") +@Tag(name = "国标设备控制") @CrossOrigin @RestController @RequestMapping("/api/device/control") @@ -40,7 +40,7 @@ public class DeviceControl { private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private SIPCommander cmder; @@ -53,26 +53,20 @@ public class DeviceControl { * * @param deviceId 设备ID */ - @ApiOperation("远程启动控制命令") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value ="设备ID", required = true, dataTypeClass = String.class), - }) + @Operation(summary = "远程启动控制命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/teleboot/{deviceId}") - public ResponseEntity teleBootApi(@PathVariable String deviceId) { + public void teleBootApi(@PathVariable String deviceId) { if (logger.isDebugEnabled()) { logger.debug("设备远程启动API调用"); } Device device = storager.queryVideoDevice(deviceId); - boolean sucsess = cmder.teleBootCmd(device); - if (sucsess) { - JSONObject json = new JSONObject(); - json.put("DeviceID", deviceId); - json.put("Result", "OK"); - return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK); - } else { - logger.warn("设备远程启动API调用失败!"); - return new ResponseEntity("设备远程启动API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); - } + try { + cmder.teleBootCmd(device); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 远程启动: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } } /** @@ -82,13 +76,10 @@ public class DeviceControl { * @param recordCmdStr Record:手动录像,StopRecord:停止手动录像 * @param channelId 通道编码(可选) */ - @ApiOperation("录像控制命令") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value ="设备ID", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value ="通道编码" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "recordCmdStr", value ="命令, 可选值:Record(手动录像),StopRecord(停止手动录像)", - required = true ,dataTypeClass = String.class), - }) + @Operation(summary = "录像控制") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "recordCmdStr", description = "命令, 可选值:Record(手动录像),StopRecord(停止手动录像)", required = true) @GetMapping("/record/{deviceId}/{recordCmdStr}") public DeferredResult> recordApi(@PathVariable String deviceId, @PathVariable String recordCmdStr, String channelId) { @@ -112,13 +103,18 @@ public class DeviceControl { return result; } resultHolder.put(key, uuid, result); - cmder.recordCmd(device, channelId, recordCmdStr, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeAllResult(msg); - }); + try { + cmder.recordCmd(device, channelId, recordCmdStr, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeAllResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 开始/停止录像: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } return result; } @@ -129,29 +125,30 @@ public class DeviceControl { * @param deviceId 设备ID * @param guardCmdStr SetGuard:布防,ResetGuard:撤防 */ - @ApiOperation("布防/撤防命令") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value ="通道编码" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "guardCmdStr", value ="命令, 可选值:SetGuard(布防),ResetGuard(撤防)", required = true, - dataTypeClass = String.class) - }) + @Operation(summary = "布防/撤防命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "guardCmdStr", description = "命令, 可选值:SetGuard(布防),ResetGuard(撤防)", required = true) @GetMapping("/guard/{deviceId}/{guardCmdStr}") - public DeferredResult> guardApi(@PathVariable String deviceId, String channelId, @PathVariable String guardCmdStr) { + public DeferredResult guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) { if (logger.isDebugEnabled()) { logger.debug("布防/撤防API调用"); } Device device = storager.queryVideoDevice(deviceId); - String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId; + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + deviceId; String uuid =UUID.randomUUID().toString(); - cmder.guardCmd(device, guardCmdStr, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(3 * 1000L); + try { + cmder.guardCmd(device, guardCmdStr, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.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("布防/撤防操作超时, 设备未返回应答指令")); @@ -173,13 +170,11 @@ public class DeviceControl { * @param alarmMethod 报警方式(可选) * @param alarmType 报警类型(可选) */ - @ApiOperation("报警复位") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value ="通道编码" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "alarmMethod", value ="报警方式", dataTypeClass = String.class), - @ApiImplicitParam(name = "alarmType", value ="报警类型", dataTypeClass = String.class), - }) + @Operation(summary = "报警复位") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "alarmMethod", description = "报警方式") + @Parameter(name = "alarmType", description = "报警类型") @GetMapping("/reset_alarm/{deviceId}") public DeferredResult> resetAlarmApi(@PathVariable String deviceId, String channelId, @RequestParam(required = false) String alarmMethod, @@ -190,14 +185,19 @@ public class DeviceControl { Device device = storager.queryVideoDevice(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId; - cmder.alarmCmd(device, alarmMethod, alarmType, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(3 * 1000L); + try { + cmder.alarmCmd(device, alarmMethod, alarmType, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 报警复位: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult> result = new DeferredResult>(3 * 1000L); result.onTimeout(() -> { logger.warn(String.format("报警复位操作超时, 设备未返回应答指令")); // 释放rtpserver @@ -217,29 +217,27 @@ public class DeviceControl { * @param deviceId 设备ID * @param channelId 通道ID */ - @ApiOperation("强制关键帧") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value ="通道ID", required = true, dataTypeClass = String.class), - }) + @Operation(summary = "强制关键帧") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号") @GetMapping("/i_frame/{deviceId}") - public ResponseEntity iFrame(@PathVariable String deviceId, + public JSONObject iFrame(@PathVariable String deviceId, @RequestParam(required = false) String channelId) { if (logger.isDebugEnabled()) { logger.debug("强制关键帧API调用"); } Device device = storager.queryVideoDevice(deviceId); - boolean sucsess = cmder.iFrameCmd(device, channelId); - if (sucsess) { - JSONObject json = new JSONObject(); - json.put("DeviceID", deviceId); - json.put("ChannelID", channelId); - json.put("Result", "OK"); - return new ResponseEntity<>(json.toJSONString(), HttpStatus.OK); - } else { - logger.warn("强制关键帧API调用失败!"); - return new ResponseEntity("强制关键帧API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); + try { + cmder.iFrameCmd(device, channelId); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 强制关键帧: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } + JSONObject json = new JSONObject(); + json.put("DeviceID", deviceId); + json.put("ChannelID", channelId); + json.put("Result", "OK"); + return json; } /** @@ -251,17 +249,14 @@ public class DeviceControl { * @param presetIndex 调用预置位编号(可选) * @param channelId 通道编码(可选) */ - @ApiOperation("看守位控制") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value ="通道编码" ,dataTypeClass = String.class), - @ApiImplicitParam(name = "enabled", value = "是否开启看守位 1:开启,0:关闭", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "resetTime", value = "自动归位时间间隔", dataTypeClass = String.class), - @ApiImplicitParam(name = "presetIndex", value = "调用预置位编号", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value ="通道ID", dataTypeClass = String.class), - }) + @Operation(summary = "看守位控制") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "enabled", description = "是否开启看守位 1:开启,0:关闭", required = true) + @Parameter(name = "presetIndex", description = "调用预置位编号") + @Parameter(name = "resetTime", description = "自动归位时间间隔") @GetMapping("/home_position/{deviceId}/{enabled}") - public DeferredResult> homePositionApi(@PathVariable String deviceId, + public DeferredResult homePositionApi(@PathVariable String deviceId, @PathVariable String enabled, @RequestParam(required = false) String resetTime, @RequestParam(required = false) String presetIndex, @@ -269,17 +264,22 @@ public class DeviceControl { if (logger.isDebugEnabled()) { logger.debug("报警复位API调用"); } - String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (StringUtils.isEmpty(channelId) ? deviceId : channelId); + String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); String uuid = UUID.randomUUID().toString(); Device device = storager.queryVideoDevice(deviceId); - cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(3 * 1000L); + try { + cmder.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 看守位控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult result = new DeferredResult<>(3 * 1000L); result.onTimeout(() -> { logger.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); // 释放rtpserver @@ -296,4 +296,106 @@ public class DeviceControl { resultHolder.put(key, uuid, result); return result; } + + /** + * 拉框放大 + * @param deviceId 设备id + * @param channelId 通道id + * @param length 播放窗口长度像素值 + * @param width 播放窗口宽度像素值 + * @param midpointx 拉框中心的横轴坐标像素值 + * @param midpointy 拉框中心的纵轴坐标像素值 + * @param lengthx 拉框长度像素值 + * @param lengthy 拉框宽度像素值 + * @return + */ + @Operation(summary = "拉框放大") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "length", description = "播放窗口长度像素值", required = true) + @Parameter(name = "midpointx", description = "拉框中心的横轴坐标像素值", required = true) + @Parameter(name = "midpointy", description = "拉框中心的纵轴坐标像素值", required = true) + @Parameter(name = "lengthx", description = "拉框长度像素值", required = true) + @Parameter(name = "lengthy", description = "lengthy", required = true) + @GetMapping("drag_zoom/zoom_in") + public void dragZoomIn(@RequestParam String deviceId, + @RequestParam(required = false) String channelId, + @RequestParam int length, + @RequestParam int width, + @RequestParam int midpointx, + @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)); + } + Device device = storager.queryVideoDevice(deviceId); + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("" + length+ "\r\n"); + cmdXml.append("" + width+ "\r\n"); + cmdXml.append("" + midpointx+ "\r\n"); + cmdXml.append("" + midpointy+ "\r\n"); + cmdXml.append("" + lengthx+ "\r\n"); + cmdXml.append("" + lengthy+ "\r\n"); + cmdXml.append("\r\n"); + try { + cmder.dragZoomCmd(device, channelId, cmdXml.toString()); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 拉框放大: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + } + + /** + * 拉框缩小 + * @param deviceId 设备id + * @param channelId 通道id + * @param length 播放窗口长度像素值 + * @param width 播放窗口宽度像素值 + * @param midpointx 拉框中心的横轴坐标像素值 + * @param midpointy 拉框中心的纵轴坐标像素值 + * @param lengthx 拉框长度像素值 + * @param lengthy 拉框宽度像素值 + * @return + */ + @Operation(summary = "拉框放大") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号") + @Parameter(name = "length", description = "播放窗口长度像素值", required = true) + @Parameter(name = "width", description = "拉框中心的横轴坐标像素值", required = true) + @Parameter(name = "midpointx", description = "拉框中心的横轴坐标像素值", required = true) + @Parameter(name = "midpointy", description = "拉框中心的纵轴坐标像素值", required = true) + @Parameter(name = "lengthx", description = "拉框长度像素值", required = true) + @Parameter(name = "lengthy", description = "拉框宽度像素值", required = true) + @GetMapping("/drag_zoom/zoom_out") + public void dragZoomOut(@RequestParam String deviceId, + @RequestParam(required = false) String channelId, + @RequestParam int length, + @RequestParam int width, + @RequestParam int midpointx, + @RequestParam int midpointy, + @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)); + } + Device device = storager.queryVideoDevice(deviceId); + StringBuffer cmdXml = new StringBuffer(200); + cmdXml.append("\r\n"); + cmdXml.append("" + length+ "\r\n"); + cmdXml.append("" + width+ "\r\n"); + cmdXml.append("" + midpointx+ "\r\n"); + cmdXml.append("" + midpointy+ "\r\n"); + cmdXml.append("" + lengthx+ "\r\n"); + cmdXml.append("" + lengthy+ "\r\n"); + cmdXml.append("\r\n"); + try { + cmder.dragZoomCmd(device, channelId, cmdXml.toString()); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.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/vmanager/gb28181/device/DeviceQuery.java index 729eca28..11bc6211 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java @@ -1,33 +1,50 @@ package com.genersoft.iot.vmp.vmanager.gb28181.device; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.DynamicTask; +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.event.DeviceOffLineDetector; +import com.genersoft.iot.vmp.gb28181.bean.SyncStatus; +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.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +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; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.compress.utils.IOUtils; 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.StringUtils; +import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; -import java.util.UUID; +import javax.servlet.http.HttpServletResponse; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.text.ParseException; +import java.util.*; -@Api(tags = "国标设备查询", value = "国标设备查询") +@Tag(name = "国标设备查询", description = "国标设备查询") @SuppressWarnings("rawtypes") @CrossOrigin @RestController @@ -37,7 +54,10 @@ public class DeviceQuery { private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; + + @Autowired + private IDeviceChannelService deviceChannelService; @Autowired private IRedisCatchStorage redisCatchStorage; @@ -47,31 +67,24 @@ public class DeviceQuery { @Autowired private DeferredResultHolder resultHolder; - - @Autowired - private DeviceOffLineDetector offLineDetector; @Autowired private IDeviceService deviceService; + @Autowired + private DynamicTask dynamicTask; + /** * 使用ID查询国标设备 * @param deviceId 国标ID * @return 国标设备 */ - @ApiOperation("使用ID查询国标设备") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", required = true, dataTypeClass = String.class), - }) + @Operation(summary = "查询国标设备") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/devices/{deviceId}") - public ResponseEntity devices(@PathVariable String deviceId){ + public Device devices(@PathVariable String deviceId){ -// if (logger.isDebugEnabled()) { -// logger.debug("查询视频设备API调用,deviceId:" + deviceId); -// } - - Device device = storager.queryVideoDevice(deviceId); - return new ResponseEntity<>(device,HttpStatus.OK); + return storager.queryVideoDevice(deviceId); } /** @@ -80,19 +93,13 @@ public class DeviceQuery { * @param count 每页查询数量 * @return 分页国标列表 */ - @ApiOperation("分页查询国标设备") - @ApiImplicitParams({ - @ApiImplicitParam(name = "page", value = "当前页", required = true, dataTypeClass = Integer.class), - @ApiImplicitParam(name = "count", value = "每页查询数量", required = true, dataTypeClass = Integer.class), - }) + @Operation(summary = "分页查询国标设备") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) @GetMapping("/devices") public PageInfo devices(int page, int count){ -// if (logger.isDebugEnabled()) { -// logger.debug("查询所有视频设备API调用"); -// } - - return storager.queryVideoDeviceList(page, count); + return storager.queryVideoDeviceList(page, count,null); } /** @@ -104,32 +111,29 @@ public class DeviceQuery { * @param query 查询内容 * @param online 是否在线 在线 true / 离线 false * @param channelType 设备 false/子目录 true + * @param catalogUnderDevice 是否直属与设备的目录 * @return 通道列表 */ - @ApiOperation("分页查询通道") @GetMapping("/devices/{deviceId}/channels") - @ApiImplicitParams({ - @ApiImplicitParam(name="deviceId", value = "设备id", required = true ,dataTypeClass = String.class), - @ApiImplicitParam(name="page", value = "当前页", required = true ,dataTypeClass = Integer.class), - @ApiImplicitParam(name="count", value = "每页查询数量", required = true ,dataTypeClass = Integer.class), - @ApiImplicitParam(name="query", value = "查询内容" ,dataTypeClass = String.class), - @ApiImplicitParam(name="online", value = "是否在线" ,dataTypeClass = Boolean.class), - @ApiImplicitParam(name="channelType", value = "设备/子目录-> false/true" ,dataTypeClass = Boolean.class), - }) - public ResponseEntity channels(@PathVariable String deviceId, + @Operation(summary = "分页查询通道") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @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) { -// if (logger.isDebugEnabled()) { -// logger.debug("查询视频设备通道API调用"); -// } - if (StringUtils.isEmpty(query)) { + @RequestParam(required = false) Boolean channelType, + @RequestParam(required = false) Boolean catalogUnderDevice) { + if (ObjectUtils.isEmpty(query)) { query = null; } - PageInfo pageResult = storager.queryChannelsByDeviceId(deviceId, query, channelType, online, page, count); - return new ResponseEntity<>(pageResult,HttpStatus.OK); + return storager.queryChannelsByDeviceId(deviceId, query, channelType, online, catalogUnderDevice, page, count); } /** @@ -137,43 +141,27 @@ public class DeviceQuery { * @param deviceId 设备id * @return */ - @ApiOperation("同步设备通道") - @ApiImplicitParams({ - @ApiImplicitParam(name="deviceId", value = "设备id", required = true, dataTypeClass = String.class), - }) - @PostMapping("/devices/{deviceId}/sync") - public DeferredResult> devicesSync(@PathVariable String deviceId){ + @Operation(summary = "同步设备通道") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @GetMapping("/devices/{deviceId}/sync") + public WVPResult devicesSync(@PathVariable String deviceId){ if (logger.isDebugEnabled()) { logger.debug("设备通道信息同步API调用,deviceId:" + deviceId); } Device device = storager.queryVideoDevice(deviceId); - String key = DeferredResultHolder.CALLBACK_CMD_CATALOG + deviceId; - String uuid = UUID.randomUUID().toString(); - DeferredResult> result = new DeferredResult>(15*1000L); - result.onTimeout(()->{ - logger.warn(String.format("设备通道信息同步超时")); - // 释放rtpserver - RequestMessage msg = new RequestMessage(); - msg.setKey(key); - msg.setId(uuid); - msg.setData("Timeout"); - resultHolder.invokeAllResult(msg); - }); - // 等待其他相同请求返回时一起返回 - if (resultHolder.exist(key, null)) { - return result; + boolean status = deviceService.isSyncRunning(deviceId); + // 已存在则返回进度 + if (status) { + SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId); + return WVPResult.success(channelSyncStatus); } - cmder.catalogQuery(device, event -> { - RequestMessage msg = new RequestMessage(); - msg.setKey(key); - msg.setId(uuid); - msg.setData(String.format("同步通道失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeAllResult(msg); - }); + deviceService.sync(device); - resultHolder.put(key, uuid, result); - return result; + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(0); + wvpResult.setMsg("开始同步"); + return wvpResult; } /** @@ -181,30 +169,37 @@ public class DeviceQuery { * @param deviceId 设备id * @return */ - @ApiOperation("移除设备") - @ApiImplicitParams({ - @ApiImplicitParam(name="deviceId", value = "设备id", required = true, dataTypeClass = String.class), - }) + @Operation(summary = "移除设备") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) @DeleteMapping("/devices/{deviceId}/delete") - public ResponseEntity delete(@PathVariable String deviceId){ + public String delete(@PathVariable String deviceId){ if (logger.isDebugEnabled()) { logger.debug("设备信息删除API调用,deviceId:" + deviceId); } - - if (offLineDetector.isOnline(deviceId)) { - return new ResponseEntity("不允许删除在线设备!", HttpStatus.NOT_ACCEPTABLE); - } + // 清除redis记录 - boolean isSuccess = storager.delete(deviceId); + boolean isSuccess = deviceService.delete(deviceId); if (isSuccess) { redisCatchStorage.clearCatchByDeviceId(deviceId); + // 停止此设备的订阅更新 + Set allKeys = dynamicTask.getAllKeys(); + for (String key : allKeys) { + if (key.startsWith(deviceId)) { + Runnable runnable = dynamicTask.get(key); + if (runnable instanceof ISubscribeTask) { + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; + subscribeTask.stop(); + } + dynamicTask.stop(key); + } + } JSONObject json = new JSONObject(); json.put("deviceId", deviceId); - return new ResponseEntity<>(json.toString(),HttpStatus.OK); + return json.toString(); } else { logger.warn("设备信息删除API调用失败!"); - return new ResponseEntity("设备信息删除API调用失败!", HttpStatus.INTERNAL_SERVER_ERROR); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备信息删除API调用失败!"); } } @@ -219,36 +214,31 @@ public class DeviceQuery { * @param channelType 通道类型 * @return 子通道列表 */ - @ApiOperation("分页查询子目录通道") - @ApiImplicitParams({ - @ApiImplicitParam(name="deviceId", value = "设备id", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name="channelId", value = "通道id", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class), - @ApiImplicitParam(name="count", value = "每页条数", required = true, dataTypeClass = Integer.class), - @ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class), - @ApiImplicitParam(name="online", value = "是否在线", dataTypeClass = String.class), - @ApiImplicitParam(name="channelType", value = "通道类型, 子目录", dataTypeClass = Boolean.class), - }) + @Operation(summary = "分页查询子目录通道") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @Parameter(name = "channelType", description = "设备/子目录-> false/true") @GetMapping("/sub_channels/{deviceId}/{channelId}/channels") - public ResponseEntity subChannels(@PathVariable String deviceId, + public PageInfo subChannels(@PathVariable String deviceId, @PathVariable String channelId, int page, int count, @RequestParam(required = false) String query, - @RequestParam(required = false) String online, + @RequestParam(required = false) Boolean online, @RequestParam(required = false) Boolean channelType){ -// if (logger.isDebugEnabled()) { -// logger.debug("查询所有视频通道API调用"); -// } DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId); if (deviceChannel == null) { PageInfo deviceChannelPageResult = new PageInfo<>(); - return new ResponseEntity<>(deviceChannelPageResult,HttpStatus.OK); + return deviceChannelPageResult; } PageInfo pageResult = storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); - return new ResponseEntity<>(pageResult,HttpStatus.OK); + return pageResult; } /** @@ -257,15 +247,12 @@ public class DeviceQuery { * @param channel 通道 * @return */ - @ApiOperation("更新通道信息") - @ApiImplicitParams({ - @ApiImplicitParam(name="deviceId", value = "设备id", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name="channel", value = "通道", required = true, dataTypeClass = String.class), - }) + @Operation(summary = "更新通道信息") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channel", description = "通道信息", required = true) @PostMapping("/channel/update/{deviceId}") - public ResponseEntity updateChannel(@PathVariable String deviceId,DeviceChannel channel){ - storager.updateChannel(deviceId, channel); - return new ResponseEntity<>(null,HttpStatus.OK); + public void updateChannel(@PathVariable String deviceId,DeviceChannel channel){ + deviceChannelService.updateChannel(deviceId, channel); } /** @@ -274,18 +261,37 @@ public class DeviceQuery { * @param streamMode 数据流传输模式 * @return */ - @ApiOperation("修改数据流传输模式") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备id", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "streamMode", value = "数据流传输模式, 取值:" + - "UDP(udp传输),TCP-ACTIVE(tcp主动模式,暂不支持),TCP-PASSIVE(tcp被动模式)", dataTypeClass = String.class), - }) + @Operation(summary = "修改数据流传输模式") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "streamMode", description = "数据流传输模式, 取值:" + + "UDP(udp传输),TCP-ACTIVE(tcp主动模式,暂不支持),TCP-PASSIVE(tcp被动模式)", required = true) @PostMapping("/transport/{deviceId}/{streamMode}") - public ResponseEntity updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ - Device device = storager.queryVideoDevice(deviceId); + public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ + Device device = deviceService.getDevice(deviceId); device.setStreamMode(streamMode); - storager.updateDevice(device); - return new ResponseEntity<>(null,HttpStatus.OK); + deviceService.updateCustomDevice(device); + } + + /** + * 添加设备信息 + * @param device 设备信息 + * @return + */ + @Operation(summary = "添加设备信息") + @Parameter(name = "device", description = "设备", required = true) + @PostMapping("/device/add/") + public void addDevice(Device device){ + + if (device == null || device.getDeviceId() == null) { + throw new ControllerException(ErrorCode.ERROR400); + } + + // 查看deviceId是否存在 + boolean exist = deviceService.isExist(device.getDeviceId()); + if (exist) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备编号已存在"); + } + deviceService.addDevice(device); } /** @@ -293,37 +299,14 @@ public class DeviceQuery { * @param device 设备信息 * @return */ - @ApiOperation("更新设备信息") - @ApiImplicitParams({ - @ApiImplicitParam(name = "device", value = "设备信息", required = true, dataTypeClass = Device.class) - }) + @Operation(summary = "更新设备信息") + @Parameter(name = "device", description = "设备", required = true) @PostMapping("/device/update/") - public ResponseEntity> updateDevice(Device device){ + public void updateDevice(Device device){ if (device != null && device.getDeviceId() != null) { - Device deviceInStore = storager.queryVideoDevice(device.getDeviceId()); - if (!StringUtils.isEmpty(device.getName())) deviceInStore.setName(device.getName()); - if (!StringUtils.isEmpty(device.getCharset())) deviceInStore.setCharset(device.getCharset()); - if (!StringUtils.isEmpty(device.getMediaServerId())) deviceInStore.setMediaServerId(device.getMediaServerId()); - - if (deviceInStore.getSubscribeCycleForCatalog() <=0 && device.getSubscribeCycleForCatalog() > 0) { - deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); - // 开启订阅 - deviceService.addCatalogSubscribe(deviceInStore); - } - if (deviceInStore.getSubscribeCycleForCatalog() > 0 && device.getSubscribeCycleForCatalog() <= 0) { - deviceInStore.setSubscribeCycleForCatalog(device.getSubscribeCycleForCatalog()); - // 取消订阅 - deviceService.removeCatalogSubscribe(deviceInStore); - } - - storager.updateDevice(deviceInStore); - cmder.deviceInfoQuery(deviceInStore); + deviceService.updateCustomDevice(device); } - WVPResult result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - return new ResponseEntity<>(result,HttpStatus.OK); } /** @@ -331,10 +314,8 @@ public class DeviceQuery { * * @param deviceId 设备id */ - @ApiOperation("设备状态查询") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备id", required = true, dataTypeClass = String.class), - }) + @Operation(summary = "设备状态查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/devices/{deviceId}/status") public DeferredResult> deviceStatusApi(@PathVariable String deviceId) { if (logger.isDebugEnabled()) { @@ -343,14 +324,23 @@ public class DeviceQuery { Device device = storager.queryVideoDevice(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId; - cmder.deviceStatusQuery(device, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult>(2*1000L); + DeferredResult> result = new DeferredResult>(2*1000L); + if(device == null) { + result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK)); + return result; + } + try { + cmder.deviceStatusQuery(device, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 获取设备状态: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } result.onTimeout(()->{ logger.warn(String.format("获取设备状态超时")); // 释放rtpserver @@ -375,16 +365,14 @@ public class DeviceQuery { * @param endTime 报警发生终止时间(可选) * @return true = 命令发送成功 */ - @ApiOperation("设备报警查询") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备id", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "startPriority", value = "报警起始级别", dataTypeClass = String.class), - @ApiImplicitParam(name = "endPriority", value = "报警终止级别", dataTypeClass = String.class), - @ApiImplicitParam(name = "alarmMethod", value = "报警方式条件", dataTypeClass = String.class), - @ApiImplicitParam(name = "alarmType", value = "报警类型", dataTypeClass = String.class), - @ApiImplicitParam(name = "startTime", value = "报警发生起始时间", dataTypeClass = String.class), - @ApiImplicitParam(name = "endTime", value = "报警发生终止时间", dataTypeClass = String.class), - }) + @Operation(summary = "设备状态查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "startPriority", description = "报警起始级别") + @Parameter(name = "endPriority", description = "报警终止级别") + @Parameter(name = "alarmMethod", description = "报警方式条件") + @Parameter(name = "alarmType", description = "报警类型") + @Parameter(name = "startTime", description = "报警发生起始时间") + @Parameter(name = "endTime", description = "报警发生终止时间") @GetMapping("/alarm/{deviceId}") public DeferredResult> alarmApi(@PathVariable String deviceId, @RequestParam(required = false) String startPriority, @@ -399,14 +387,19 @@ public class DeviceQuery { Device device = storager.queryVideoDevice(deviceId); String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId; String uuid = UUID.randomUUID().toString(); - cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("设备报警查询失败,错误码: %s, %s",event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); - DeferredResult> result = new DeferredResult> (3 * 1000L); + try { + cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("设备报警查询失败,错误码: %s, %s",event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 设备报警查询: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + DeferredResult> result = new DeferredResult> (3 * 1000L); result.onTimeout(()->{ logger.warn(String.format("设备报警查询超时")); // 释放rtpserver @@ -421,4 +414,152 @@ public class DeviceQuery { } + @GetMapping("/{deviceId}/sync_status") + @Operation(summary = "获取通道同步进度") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + public WVPResult getSyncStatus(@PathVariable String deviceId) { + SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId); + WVPResult wvpResult = new WVPResult<>(); + if (channelSyncStatus == null) { + wvpResult.setCode(-1); + wvpResult.setMsg("同步尚未开始"); + }else { + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); + wvpResult.setData(channelSyncStatus); + if (channelSyncStatus.getErrorMsg() != null) { + wvpResult.setMsg(channelSyncStatus.getErrorMsg()); + } + } + return wvpResult; + } + + @GetMapping("/{deviceId}/subscribe_info") + @Operation(summary = "获取设备的订阅状态") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + public WVPResult> getSubscribeInfo(@PathVariable String deviceId) { + Set allKeys = dynamicTask.getAllKeys(); + Map dialogStateMap = new HashMap<>(); + for (String key : allKeys) { + if (key.startsWith(deviceId)) { + ISubscribeTask subscribeTask = (ISubscribeTask)dynamicTask.get(key); + if (subscribeTask instanceof CatalogSubscribeTask) { + dialogStateMap.put("catalog", 1); + }else if (subscribeTask instanceof MobilePositionSubscribeTask) { + dialogStateMap.put("mobilePosition", 1); + } + } + } + WVPResult> wvpResult = new WVPResult<>(); + wvpResult.setCode(0); + wvpResult.setData(dialogStateMap); + return wvpResult; + } + + @GetMapping("/snap/{deviceId}/{channelId}") + @Operation(summary = "请求截图") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId) { + + try { + final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + ".jpg").toPath()); + resp.setContentType(MediaType.IMAGE_PNG_VALUE); + IOUtils.copy(in, resp.getOutputStream()); + } catch (IOException e) { + resp.setStatus(HttpServletResponse.SC_NOT_FOUND); + } + } + + /** + * 查询国标树 + * @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); + } } 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 index fcfc8471..447944a7 100644 --- 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 @@ -1,20 +1,22 @@ package com.genersoft.iot.vmp.vmanager.gb28181.gbStream; import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean.GbStreamParam; import com.genersoft.iot.vmp.service.IGbStreamService; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.vmanager.gb28181.gbStream.bean.GbStreamParam; import com.github.pagehelper.PageInfo; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +import 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.ObjectUtils; import org.springframework.web.bind.annotation.*; -@Api(tags = "视频流关联到级联平台") +import java.util.List; + +@Tag(name = "视频流关联到级联平台") @CrossOrigin @RestController @RequestMapping("/api/gbStream") @@ -26,26 +28,44 @@ public class GbStreamController { private IGbStreamService gbStreamService; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; /** * 查询国标通道 * @param page 当前页 * @param count 每页条数 + * @param platformId 平台ID * @return */ - @ApiOperation("查询国标通道") - @ApiImplicitParams({ - @ApiImplicitParam(name = "page", value = "当前页", required = true , dataTypeClass = Integer.class), - @ApiImplicitParam(name = "count", value = "每页条数", required = true , dataTypeClass = Integer.class), - }) + @Operation(summary = "查询国标通道") + @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 = false)Integer page, - @RequestParam(required = false)Integer count){ + 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; + } - return gbStreamService.getAll(page, count); + // catalogId 为null 查询未在平台下分配的数据 + // catalogId 不为null 查询平台下这个,目录下的通道 + return gbStreamService.getAll(page, count, platformId, catalogId, query, mediaServerId); } @@ -54,18 +74,17 @@ public class GbStreamController { * @param gbStreamParam * @return */ - @ApiOperation("移除国标关联") - @ApiImplicitParams({ - @ApiImplicitParam(name = "gbStreamParam", value = "GbStreamParam", required = true, - dataTypeClass = GbStreamParam.class), - }) + @Operation(summary = "移除国标关联") @DeleteMapping(value = "/del") @ResponseBody - public Object del(@RequestBody GbStreamParam gbStreamParam){ - if (gbStreamService.delPlatformInfo(gbStreamParam.getGbStreams())) { - return "success"; + public void del(@RequestBody GbStreamParam gbStreamParam){ + + if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().size() == 0) { + if (gbStreamParam.isAll()) { + gbStreamService.delAllPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); + } }else { - return "fail"; + gbStreamService.delPlatformInfo(gbStreamParam.getPlatformId(), gbStreamParam.getGbStreams()); } } @@ -75,17 +94,17 @@ public class GbStreamController { * @param gbStreamParam * @return */ - @ApiOperation("保存国标关联") - @ApiImplicitParams({ - @ApiImplicitParam(name = "gbStreamParam", value = "GbStreamParam", required = true, dataTypeClass = GbStreamParam.class), - }) + @Operation(summary = "保存国标关联") @PostMapping(value = "/add") @ResponseBody - public Object add(@RequestBody GbStreamParam gbStreamParam){ - if (gbStreamService.addPlatformInfo(gbStreamParam.getGbStreams(), gbStreamParam.getPlatformId())) { - return "success"; + public void add(@RequestBody GbStreamParam gbStreamParam){ + if (gbStreamParam.getGbStreams() == null || gbStreamParam.getGbStreams().size() == 0) { + if (gbStreamParam.isAll()) { + List allGBChannels = gbStreamService.getAllGBChannels(gbStreamParam.getPlatformId()); + gbStreamService.addPlatformInfo(allGBChannels, gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); + } }else { - return "fail"; + gbStreamService.addPlatformInfo(gbStreamParam.getGbStreams(), gbStreamParam.getPlatformId(), gbStreamParam.getCatalogId()); } } } 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 index 40456a85..3705c709 100644 --- 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 @@ -1,19 +1,37 @@ 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; } @@ -25,4 +43,12 @@ public class GbStreamParam { 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/media/MediaController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java index d5caab29..d135fa2f 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java @@ -1,22 +1,28 @@ package com.genersoft.iot.vmp.vmanager.gb28181.media; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.service.IStreamPushService; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.conf.security.SecurityUtils; +import com.genersoft.iot.vmp.conf.security.dto.LoginUser; +import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; import com.genersoft.iot.vmp.service.IMediaService; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +import com.genersoft.iot.vmp.service.IStreamProxyService; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +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.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.web.bind.annotation.*; +import javax.servlet.http.HttpServletRequest; -@Api(tags = "媒体流相关") + +@Tag(name = "媒体流相关") @Controller @CrossOrigin @RequestMapping(value = "/api/media") @@ -25,13 +31,12 @@ public class MediaController { private final static Logger logger = LoggerFactory.getLogger(MediaController.class); @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IStreamPushService streamPushService; + private IRedisCatchStorage redisCatchStorage; @Autowired private IMediaService mediaService; + @Autowired + private IStreamProxyService streamProxyService; /** @@ -40,28 +45,73 @@ public class MediaController { * @param stream 流id * @return */ - @ApiOperation("根据应用名和流id获取播放地址") - @ApiImplicitParams({ - @ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class), - @ApiImplicitParam(name = "stream", value = "流id", dataTypeClass = String.class), - @ApiImplicitParam(name = "mediaServerId", value = "媒体服务器id", dataTypeClass = String.class, required = false), - }) + @Operation(summary = "根据应用名和流id获取播放地址") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + @Parameter(name = "mediaServerId", description = "媒体服务器id") + @Parameter(name = "callId", description = "推流时携带的自定义鉴权ID") + @Parameter(name = "useSourceIpAsStreamIp", description = "是否使用请求IP作为返回的地址IP") @GetMapping(value = "/stream_info_by_app_and_stream") @ResponseBody - public WVPResult getStreamInfoByAppAndStream(@RequestParam String app, @RequestParam String stream, @RequestParam(required = false) String mediaServerId){ - StreamInfo streamInfoByAppAndStreamWithCheck = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId); - WVPResult result = new WVPResult<>(); - if (streamInfoByAppAndStreamWithCheck != null){ - result.setCode(0); - result.setMsg("scccess"); - result.setData(streamInfoByAppAndStreamWithCheck); + public StreamContent getStreamInfoByAppAndStream(HttpServletRequest request, @RequestParam String app, + @RequestParam String stream, + @RequestParam(required = false) String mediaServerId, + @RequestParam(required = false) String callId, + @RequestParam(required = false) Boolean useSourceIpAsStreamIp){ + boolean authority = false; + if (callId != null) { + // 权限校验 + StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream); + if (streamAuthorityInfo != null + && streamAuthorityInfo.getCallId() != null + && streamAuthorityInfo.getCallId().equals(callId)) { + authority = true; + }else { + throw new ControllerException(ErrorCode.ERROR400); + } }else { - result.setCode(-1); - result.setMsg("fail"); + // 是否登陆用户, 登陆用户返回完整信息 + LoginUser userInfo = SecurityUtils.getUserInfo(); + if (userInfo!= null) { + authority = true; + } + } + + StreamInfo streamInfo; + + if (useSourceIpAsStreamIp != null && useSourceIpAsStreamIp) { + String host = request.getHeader("Host"); + String localAddr = host.split(":")[0]; + logger.info("使用{}作为返回流的ip", localAddr); + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, localAddr, authority); + }else { + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); + } + + if (streamInfo != null){ + return new StreamContent(streamInfo); + }else { + //获取流失败,重启拉流后重试一次 + streamProxyService.stop(app,stream); + boolean start = streamProxyService.start(app, stream); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error("[线程休眠失败], {}", e.getMessage()); + } + if (useSourceIpAsStreamIp != null && useSourceIpAsStreamIp) { + String host = request.getHeader("Host"); + String localAddr = host.split(":")[0]; + logger.info("使用{}作为返回流的ip", localAddr); + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, localAddr, authority); + }else { + streamInfo = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); + } + if (streamInfo != null){ + return new StreamContent(streamInfo); + }else { + throw new ControllerException(ErrorCode.ERROR100); + } } - return result; } - - - } 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 index 5af08375..40b65c33 100644 --- 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 @@ -1,30 +1,42 @@ package com.genersoft.iot.vmp.vmanager.gb28181.platform; -import com.alibaba.fastjson.JSONObject; +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.gb28181.bean.ParentPlatform; +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.IVideoManagerStorager; +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.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +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.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.util.StringUtils; +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; + /** * 级联平台管理 */ -@Api(tags = "级联平台管理") +@Tag(name = "级联平台管理") @CrossOrigin @RestController @RequestMapping("/api/platform") @@ -33,250 +45,546 @@ public class PlatformController { private final static Logger logger = LoggerFactory.getLogger(PlatformController.class); @Autowired - private IVideoManagerStorager storager; + 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 SipConfig sipConfig; + private DynamicTask dynamicTask; + + @Autowired + private IPlatformService platformService; + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private IGbStreamService gbStreamService; /** * 获取国标服务的配置 + * * @return */ - @ApiOperation("获取国标服务的配置") + @Operation(summary = "获取国标服务的配置") @GetMapping("/server_config") - public ResponseEntity serverConfig() { + public JSONObject serverConfig() { JSONObject result = new JSONObject(); result.put("deviceIp", sipConfig.getIp()); result.put("devicePort", sipConfig.getPort()); result.put("username", sipConfig.getId()); result.put("password", sipConfig.getPassword()); - return new ResponseEntity<>(result, HttpStatus.OK); + return result; + } + + /** + * 获取级联服务器信息 + * + * @return + */ + @Operation(summary = "获取级联服务器信息") + @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 page 当前页 * @param count 每页条数 * @return */ - @ApiOperation("分页查询级联平台") @GetMapping("/query/{count}/{page}") - @ApiImplicitParams({ - @ApiImplicitParam(name = "page", value = "当前页", dataTypeClass = Integer.class), - @ApiImplicitParam(name = "count", value = "每页条数", dataTypeClass = Integer.class), - }) - public PageInfo platforms(@PathVariable int page, @PathVariable int count){ + @Operation(summary = "分页查询级联平台") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页条数", required = true) + public PageInfo platforms(@PathVariable int page, @PathVariable int count) { -// if (logger.isDebugEnabled()) { -// logger.debug("查询所有上级设备API调用"); -// } - return storager.queryParentPlatformList(page, 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 */ - @ApiOperation("保存上级平台信息") - @ApiImplicitParams({ - @ApiImplicitParam(name = "parentPlatform", value = "上级平台信息", dataTypeClass = ParentPlatform.class), - }) - @PostMapping("/save") + @Operation(summary = "添加上级平台信息") + @PostMapping("/add") @ResponseBody - public ResponseEntity savePlatform(@RequestBody ParentPlatform parentPlatform){ + public void addPlatform(@RequestBody ParentPlatform parentPlatform) { if (logger.isDebugEnabled()) { logger.debug("保存上级平台信息API调用"); } - if (StringUtils.isEmpty(parentPlatform.getName()) - ||StringUtils.isEmpty(parentPlatform.getServerGBId()) - ||StringUtils.isEmpty(parentPlatform.getServerGBDomain()) - ||StringUtils.isEmpty(parentPlatform.getServerIP()) - ||StringUtils.isEmpty(parentPlatform.getServerPort()) - ||StringUtils.isEmpty(parentPlatform.getDeviceGBId()) - ||StringUtils.isEmpty(parentPlatform.getExpires()) - ||StringUtils.isEmpty(parentPlatform.getKeepTimeout()) - ||StringUtils.isEmpty(parentPlatform.getTransport()) - ||StringUtils.isEmpty(parentPlatform.getCharacterSet()) - ){ - return new ResponseEntity<>("missing parameters", HttpStatus.BAD_REQUEST); + 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"); } - // TODO 检查是否已经存在,且注册成功, 如果注册成功,需要先注销之前再,修改并注册 - // ParentPlatform parentPlatformOld = storager.queryParentPlatById(parentPlatform.getDeviceGBId()); + 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 = "保存上级平台信息") + @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); + } + parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase()); + ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId()); + parentPlatform.setUpdateTime(DateUtil.getNow()); + if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) { + // 目录结构发生变化,清空之前的关联关系 + logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId()); + storager.cleanContentForPlatform(parentPlatform.getServerGBId()); + + } boolean updateResult = storager.updateParentPlatform(parentPlatform); if (updateResult) { // 保存时启用就发送注册 if (parentPlatform.isEnable()) { - // 只要保存就发送注册 - commanderForPlatform.register(parentPlatform, null, null); - } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()){ // 关闭启用时注销 - commanderForPlatform.unregister(parentPlatform, null, null); + if (parentPlatformOld != null && parentPlatformOld.isStatus()) { + try { + commanderForPlatform.unregister(parentPlatformOld, null, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + logger.error("[线程休眠失败] : {}", e.getMessage()); + } + // 只要保存就发送注册 + try { + commanderForPlatform.register(parentPlatform, null, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage()); + } + + } else { + // 只要保存就发送注册 + try { + commanderForPlatform.register(parentPlatform, null, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage()); + } + } + } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()) { // 关闭启用时注销 + try { + commanderForPlatform.unregister(parentPlatformOld, null, null); + } catch (InvalidArgumentException | ParseException | SipException e) { + logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); + } + // 停止订阅相关的定时任务 + subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId()); } - return new ResponseEntity<>("success", HttpStatus.OK); } else { - return new ResponseEntity<>("fail", HttpStatus.OK); + throw new ControllerException(ErrorCode.ERROR100.getCode(),"写入数据库失败"); } } /** * 删除上级平台 - * @param serverGBId 上级平台国标ID + * + * @param serverGBId 上级平台国标ID * @return */ - @ApiOperation("删除上级平台") - @ApiImplicitParams({ - @ApiImplicitParam(name = "serverGBId", value = "上级平台国标ID", dataTypeClass = String.class), - }) + @Operation(summary = "删除上级平台") + @Parameter(name = "serverGBId", description = "上级平台的国标编号") @DeleteMapping("/delete/{serverGBId}") @ResponseBody - public ResponseEntity deletePlatform(@PathVariable String serverGBId){ + public void deletePlatform(@PathVariable String serverGBId) { if (logger.isDebugEnabled()) { logger.debug("删除上级平台API调用"); } - if (StringUtils.isEmpty(serverGBId) - ){ - return new ResponseEntity<>("missing parameters", HttpStatus.BAD_REQUEST); + if (ObjectUtils.isEmpty(serverGBId) + ) { + throw new ControllerException(ErrorCode.ERROR400); } ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId); - if (parentPlatform == null) return new ResponseEntity<>("fail", HttpStatus.OK); + if (parentPlatform == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在"); + } // 发送离线消息,无论是否成功都删除缓存 - commanderForPlatform.unregister(parentPlatform, (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()); - })); + try { + commanderForPlatform.unregister(parentPlatform, (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); - - - if (deleteResult) { - return new ResponseEntity<>("success", HttpStatus.OK); - }else { - return new ResponseEntity<>("fail", HttpStatus.OK); + 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 */ - @ApiOperation("查询上级平台是否存在") - @ApiImplicitParams({ - @ApiImplicitParam(name = "serverGBId", value = "上级平台国标ID", dataTypeClass = String.class), - }) + @Operation(summary = "查询上级平台是否存在") + @Parameter(name = "serverGBId", description = "上级平台的国标编号") @GetMapping("/exit/{serverGBId}") @ResponseBody - public ResponseEntity exitPlatform(@PathVariable String serverGBId){ + public Boolean exitPlatform(@PathVariable String serverGBId) { -// if (logger.isDebugEnabled()) { -// logger.debug("查询上级平台是否存在API调用:" + serverGBId); -// } ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId); - return new ResponseEntity<>(String.valueOf(parentPlatform != null), HttpStatus.OK); + return parentPlatform != null; } /** * 分页查询级联平台的所有所有通道 - * @param page 当前页 - * @param count 每页条数 - * @param platformId 上级平台ID - * @param query 查询内容 - * @param online 是否在线 - * @param choosed 是否已选中 + * + * @param page 当前页 + * @param count 每页条数 + * @param platformId 上级平台ID + * @param query 查询内容 + * @param online 是否在线 * @param channelType 通道类型 * @return */ - @ApiOperation("分页查询级联平台的所有所有通道") - @ApiImplicitParams({ - @ApiImplicitParam(name = "page", value = "当前页", dataTypeClass = Integer.class), - @ApiImplicitParam(name = "count", value = "每页条数", dataTypeClass = Integer.class), - @ApiImplicitParam(name = "platformId", value = "上级平台ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "query", value = "查询内容", dataTypeClass = String.class), - @ApiImplicitParam(name = "online", value = "是否在线", dataTypeClass = Boolean.class), - @ApiImplicitParam(name = "choosed", value = "是否已选中", dataTypeClass = Boolean.class), - @ApiImplicitParam(name = "channelType", value = "通道类型", dataTypeClass = Boolean.class), - }) + @Operation(summary = "查询上级平台是否存在") + @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 query, - @RequestParam(required = false) Boolean online, - @RequestParam(required = false) Boolean choosed, - @RequestParam(required = false) Boolean channelType){ + @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 (logger.isDebugEnabled()) { -// logger.debug("查询所有所有通道API调用"); -// } - PageInfo channelReduces = null; - if (platformId != null ) { - channelReduces = storager.queryAllChannelList(page, count, query, online, channelType, platformId, choosed); - }else { - channelReduces = storager.queryAllChannelList(page, count, query, online, channelType, null, false); + 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 */ - @ApiOperation("向上级平台添加国标通道") - @ApiImplicitParams({ - @ApiImplicitParam(name = "param", value = "通道关联参数", dataTypeClass = UpdateChannelParam.class), - }) + @Operation(summary = "向上级平台添加国标通道") @PostMapping("/update_channel_for_gb") @ResponseBody - public ResponseEntity updateChannelForGB(@RequestBody UpdateChannelParam param){ + public void updateChannelForGB(@RequestBody UpdateChannelParam param) { if (logger.isDebugEnabled()) { logger.debug("给上级平台添加国标通道API调用"); } - int result = storager.updateChannelForGB(param.getPlatformId(), param.getChannelReduces()); - - return new ResponseEntity<>(String.valueOf(result > 0), HttpStatus.OK); + 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 */ - @ApiOperation("从上级平台移除国标通道") - @ApiImplicitParams({ - @ApiImplicitParam(name = "param", value = "通道关联参数", dataTypeClass = UpdateChannelParam.class), - }) + @Operation(summary = "从上级平台移除国标通道") @DeleteMapping("/del_channel_for_gb") @ResponseBody - public ResponseEntity delChannelForGB(@RequestBody UpdateChannelParam param){ + public void delChannelForGB(@RequestBody UpdateChannelParam param) { if (logger.isDebugEnabled()) { logger.debug("给上级平台删除国标通道API调用"); } - int result = storager.delChannelForGB(param.getPlatformId(), param.getChannelReduces()); + 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); + } + } - return new ResponseEntity<>(String.valueOf(result > 0), HttpStatus.OK); + /** + * 获取目录 + * + * @param platformId 平台ID + * @param parentId 目录父ID + * @return + */ + @Operation(summary = "获取目录") + @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(); + } + + return storager.getChildrenCatalogByPlatform(platformId, parentId); + } + + /** + * 添加目录 + * + * @param platformCatalog 目录 + * @return + */ + @Operation(summary = "添加目录") + @PostMapping("/catalog/add") + @ResponseBody + public void addCatalog(@RequestBody PlatformCatalog platformCatalog) { + + if (logger.isDebugEnabled()) { + logger.debug("添加目录,{}", JSON.toJSONString(platformCatalog)); + } + PlatformCatalog platformCatalogInStore = storager.getCatalog(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 = "编辑目录") + @PostMapping("/catalog/edit") + @ResponseBody + public void editCatalog(@RequestBody PlatformCatalog platformCatalog) { + + if (logger.isDebugEnabled()) { + logger.debug("编辑目录,{}", JSON.toJSONString(platformCatalog)); + } + PlatformCatalog platformCatalogInStore = storager.getCatalog(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 = "删除目录") + @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(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 = "删除关联") + @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 = "修改默认目录") + @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/ChannelReduce.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/ChannelReduce.java index d4ef0bcb..2b01d45b 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/ChannelReduce.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/bean/ChannelReduce.java @@ -1,45 +1,75 @@ package com.genersoft.iot.vmp.vmanager.gb28181.platform.bean; +import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import io.swagger.v3.oas.annotations.media.Schema; + /** * 精简的channel信息展示,主要是选择通道的时候展示列表使用 */ +@Schema(description = "精简的channel信息展示") public class ChannelReduce { + /** + * deviceChannel的数据库自增ID + */ + @Schema(description = "deviceChannel的数据库自增ID") + private int id; + /** * 通道id */ + @Schema(description = "通道国标编号") private String channelId; /** * 设备id */ + @Schema(description = "设备国标编号") private String deviceId; /** * 通道名 */ + @Schema(description = "通道名") private String name; /** * 生产厂商 */ + @Schema(description = "生产厂商") private String manufacturer; /** * wan地址 */ + @Schema(description = "wan地址") private String hostAddress; /** * 子节点数 */ + @Schema(description = "子节点数") private int subCount; /** * 平台Id */ + @Schema(description = "平台上级国标编号") private String platformId; + /** + * 目录Id + */ + @Schema(description = "目录国标编号") + private String catalogId; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } public String getChannelId() { return channelId; @@ -96,4 +126,12 @@ public class ChannelReduce { public void setPlatformId(String platformId) { this.platformId = platformId; } + + public String getCatalogId() { + return catalogId; + } + + public void setCatalogId(String catalogId) { + this.catalogId = catalogId; + } } 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 index 445b08b7..2d598e44 100644 --- 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 @@ -1,9 +1,26 @@ 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() { @@ -21,4 +38,20 @@ public class UpdateChannelParam { 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/gb28181/platformGbStream/PlatformGbStreamController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platformGbStream/PlatformGbStreamController.java deleted file mode 100644 index 0eeea8b8..00000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platformGbStream/PlatformGbStreamController.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.platformGbStream; - -import com.genersoft.iot.vmp.gb28181.bean.GbStream; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.service.IGbStreamService; -import com.github.pagehelper.PageInfo; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -@Api(tags = "级联平台关联视频流") -@CrossOrigin -@RestController -@RequestMapping("/api/platform_gb_stream") -public class PlatformGbStreamController { - - private final static Logger logger = LoggerFactory.getLogger(PlatformGbStreamController.class); - - @Autowired - private IGbStreamService gbStreamService; - - @Autowired - private IVideoManagerStorager storager; - - @ApiOperation("分页查询级联平台关联的视频流") - @ApiImplicitParams({ - @ApiImplicitParam(name = "page", value = "当前页", dataTypeClass = Integer.class), - @ApiImplicitParam(name = "count", value = "每页条数", dataTypeClass = Integer.class), - }) - @GetMapping(value = "/list") - @ResponseBody - public PageInfo list(@RequestParam(required = false)Integer page, - @RequestParam(required = false)Integer count){ - - return gbStreamService.getAll(page, count); - } - - -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java index c22a5584..c94dbf2c 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java @@ -1,47 +1,45 @@ package com.genersoft.iot.vmp.vmanager.gb28181.play; -import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson2.JSONArray; +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.exception.SsrcTransactionNotFoundException; +import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager; -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.impl.SIPCommander; import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.service.IMediaServerService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; import com.genersoft.iot.vmp.service.IMediaService; import com.genersoft.iot.vmp.service.IPlayService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +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.vmanager.bean.StreamContent; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +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.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; +import javax.servlet.http.HttpServletRequest; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; import java.util.List; import java.util.UUID; -import javax.sip.message.Response; - -@Api(tags = "国标设备点播") +@Tag(name = "国标设备点播") @CrossOrigin @RestController @RequestMapping("/api/play") @@ -56,7 +54,7 @@ public class PlayController { private VideoStreamSessionManager streamSession; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private IRedisCatchStorage redisCatchStorage; @@ -76,198 +74,187 @@ public class PlayController { @Autowired private IMediaServerService mediaServerService; - @ApiOperation("开始点播") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - }) + @Autowired + private UserSetting userSetting; + + @Operation(summary = "开始点播") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) @GetMapping("/start/{deviceId}/{channelId}") - public DeferredResult> play(@PathVariable String deviceId, - @PathVariable String channelId) { + public DeferredResult> play(HttpServletRequest request, @PathVariable String deviceId, + @PathVariable String channelId) { // 获取可用的zlm Device device = storager.queryVideoDevice(deviceId); MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); - PlayResult playResult = playService.play(newMediaServerItem, deviceId, channelId, null, null); - return playResult.getResult(); + RequestMessage msg = new RequestMessage(); + String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId; + boolean exist = resultHolder.exist(key, null); + msg.setKey(key); + String uuid = UUID.randomUUID().toString(); + msg.setId(uuid); + DeferredResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + DeferredResultEx> deferredResultEx = new DeferredResultEx<>(result); + + result.onTimeout(()->{ + logger.info("点播接口等待超时"); + // 释放rtpserver + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("点播超时"); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); + // TODO 在点播未成功的情况下在此调用接口点播会导致返回的流地址ip错误 + deferredResultEx.setFilter(result1 -> { + WVPResult wvpResult1 = (WVPResult)result1; + WVPResult resultStream = new WVPResult<>(); + resultStream.setCode(wvpResult1.getCode()); + resultStream.setMsg(wvpResult1.getMsg()); + if (wvpResult1.getCode() == ErrorCode.SUCCESS.getCode()) { + StreamInfo data = wvpResult1.getData().clone(); + if (userSetting.getUseSourceIpAsStreamIp()) { + data.channgeStreamIp(request.getLocalName()); + } + resultStream.setData(new StreamContent(wvpResult1.getData())); + } + return resultStream; + }); + + + // 录像查询以channelId作为deviceId查询 + resultHolder.put(key, uuid, deferredResultEx); + + if (!exist) { + playService.play(newMediaServerItem, deviceId, channelId, null, null, null); + } + return result; } - @ApiOperation("停止点播") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - }) + @Operation(summary = "停止点播") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) @GetMapping("/stop/{deviceId}/{channelId}") - public DeferredResult> playStop(@PathVariable String deviceId, @PathVariable String channelId) { + public JSONObject playStop(@PathVariable String deviceId, @PathVariable String channelId) { logger.debug(String.format("设备预览/回放停止API调用,streamId:%s_%s", deviceId, channelId )); - String uuid = UUID.randomUUID().toString(); - DeferredResult> result = new DeferredResult>(); - - // 录像查询以channelId作为deviceId查询 - String key = DeferredResultHolder.CALLBACK_CMD_STOP + deviceId + channelId; - resultHolder.put(key, uuid, result); - Device device = storager.queryVideoDevice(deviceId); - cmder.streamByeCmd(deviceId, channelId, (event) -> { - StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); - if (streamInfo == null) { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData("点播未找到"); - resultHolder.invokeAllResult(msg); - storager.stopPlay(deviceId, channelId); - }else { - redisCatchStorage.stopPlay(streamInfo); - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - //Response response = event.getResponse(); - msg.setData(String.format("success")); - resultHolder.invokeAllResult(msg); - } - mediaServerService.closeRTPServer(device, channelId); - }); - - if (deviceId != null || channelId != null) { - JSONObject json = new JSONObject(); - json.put("deviceId", deviceId); - json.put("channelId", channelId); - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(json.toString()); - resultHolder.invokeAllResult(msg); - } else { - logger.warn("设备预览/回放停止API调用失败!"); - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData("streamId null"); - resultHolder.invokeAllResult(msg); + if (deviceId == null || channelId == null) { + throw new ControllerException(ErrorCode.ERROR400); } - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备预览/回放停止超时,deviceId/channelId:%s_%s ", deviceId, channelId)); - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData("Timeout"); - resultHolder.invokeAllResult(msg); - }); - return result; + Device device = storager.queryVideoDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备[" + deviceId + "]不存在"); + } + + StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId); + if (streamInfo == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到"); + } + + try { + logger.warn("[停止点播] {}/{}", device.getDeviceId(), channelId); + cmder.streamByeCmd(device, channelId, streamInfo.getStream(), null, null); + } catch (InvalidArgumentException | SipException | ParseException | SsrcTransactionNotFoundException e) { + logger.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } + redisCatchStorage.stopPlay(streamInfo); + + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); + JSONObject json = new JSONObject(); + json.put("deviceId", deviceId); + json.put("channelId", channelId); + return json; } /** * 将不是h264的视频通过ffmpeg 转码为h264 + aac * @param streamId 流ID - * @return */ - @ApiOperation("将不是h264的视频通过ffmpeg 转码为h264 + aac") - @ApiImplicitParams({ - @ApiImplicitParam(name = "streamId", value = "视频流ID", dataTypeClass = String.class), - }) + @Operation(summary = "将不是h264的视频通过ffmpeg 转码为h264 + aac") + @Parameter(name = "streamId", description = "视频流ID", required = true) @PostMapping("/convert/{streamId}") - public ResponseEntity playConvert(@PathVariable String streamId) { + public JSONObject playConvert(@PathVariable String streamId) { StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId); if (streamInfo == null) { - streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); + streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); } if (streamInfo == null) { logger.warn("视频转码API调用失败!, 视频流已经停止!"); - return new ResponseEntity("未找到视频流信息, 视频流可能已经停止", HttpStatus.OK); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到视频流信息, 视频流可能已经停止"); } MediaServerItem mediaInfo = mediaServerService.getOne(streamInfo.getMediaServerId()); JSONObject rtpInfo = zlmresTfulUtils.getRtpInfo(mediaInfo, streamId); if (!rtpInfo.getBoolean("exist")) { logger.warn("视频转码API调用失败!, 视频流已停止推流!"); - return new ResponseEntity("推流信息在流媒体中不存在, 视频流可能已停止推流", HttpStatus.OK); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到视频流信息, 视频流可能已停止推流"); } else { String dstUrl = String.format("rtmp://%s:%s/convert/%s", "127.0.0.1", mediaInfo.getRtmpPort(), streamId ); String srcUrl = String.format("rtsp://%s:%s/rtp/%s", "127.0.0.1", mediaInfo.getRtspPort(), streamId); JSONObject jsonObject = zlmresTfulUtils.addFFmpegSource(mediaInfo, srcUrl, dstUrl, "1000000", true, false, null); logger.info(jsonObject.toJSONString()); - JSONObject result = new JSONObject(); if (jsonObject != null && jsonObject.getInteger("code") == 0) { - result.put("code", 0); JSONObject data = jsonObject.getJSONObject("data"); if (data != null) { - result.put("key", data.getString("key")); - StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStreamWithCheck("convert", streamId, mediaInfo.getId()); - result.put("data", streamInfoResult); + JSONObject result = new JSONObject(); + result.put("key", data.getString("key")); + StreamInfo streamInfoResult = mediaService.getStreamInfoByAppAndStreamWithCheck("convert", streamId, mediaInfo.getId(), false); + result.put("StreamInfo", streamInfoResult); + return result; + }else { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "转码失败"); } }else { - result.put("code", 1); - result.put("msg", "cover fail"); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "转码失败"); } - return new ResponseEntity( result.toJSONString(), HttpStatus.OK); } } /** * 结束转码 - * @param key - * @return */ - @ApiOperation("结束转码") - @ApiImplicitParams({ - @ApiImplicitParam(name = "key", value = "视频流key", dataTypeClass = String.class), - }) + @Operation(summary = "结束转码") + @Parameter(name = "key", description = "视频流key", required = true) + @Parameter(name = "mediaServerId", description = "流媒体服务ID", required = true) @PostMapping("/convertStop/{key}") - public ResponseEntity playConvertStop(@PathVariable String key, String mediaServerId) { - JSONObject result = new JSONObject(); + public void playConvertStop(@PathVariable String key, String mediaServerId) { if (mediaServerId == null) { - result.put("code", 400); - result.put("msg", "mediaServerId is null"); - return new ResponseEntity( result.toJSONString(), HttpStatus.BAD_REQUEST); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "流媒体:" + mediaServerId + "不存在" ); } MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId); if (mediaInfo == null) { - result.put("code", 0); - result.put("msg", "使用的流媒体已经停止运行"); - return new ResponseEntity( result.toJSONString(), HttpStatus.OK); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "使用的流媒体已经停止运行" ); }else { JSONObject jsonObject = zlmresTfulUtils.delFFmpegSource(mediaInfo, key); logger.info(jsonObject.toJSONString()); if (jsonObject != null && jsonObject.getInteger("code") == 0) { - result.put("code", 0); JSONObject data = jsonObject.getJSONObject("data"); - if (data != null && data.getBoolean("flag")) { - result.put("code", "0"); - result.put("msg", "success"); - }else { - + if (data == null || data.getBoolean("flag") == null || !data.getBoolean("flag")) { + throw new ControllerException(ErrorCode.ERROR100 ); } }else { - result.put("code", 1); - result.put("msg", "delFFmpegSource fail"); + throw new ControllerException(ErrorCode.ERROR100 ); } - return new ResponseEntity( result.toJSONString(), HttpStatus.OK); } - - } - @ApiOperation("语音广播命令") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备Id", dataTypeClass = String.class), - }) + @Operation(summary = "语音广播命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) @GetMapping("/broadcast/{deviceId}") @PostMapping("/broadcast/{deviceId}") - public DeferredResult> broadcastApi(@PathVariable String deviceId) { + public DeferredResult broadcastApi(@PathVariable String deviceId) { if (logger.isDebugEnabled()) { logger.debug("语音广播API调用"); } Device device = storager.queryVideoDevice(deviceId); - DeferredResult> result = new DeferredResult>(3 * 1000L); + DeferredResult result = new DeferredResult<>(3 * 1000L); String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + deviceId; if (resultHolder.exist(key, null)) { - result.setResult(new ResponseEntity<>("设备使用中",HttpStatus.OK)); + result.setResult("设备使用中"); return result; } String uuid = UUID.randomUUID().toString(); @@ -286,21 +273,26 @@ public class PlayController { resultHolder.invokeResult(msg); return result; } - cmder.audioBroadcastCmd(device, (event) -> { - RequestMessage msg = new RequestMessage(); - msg.setKey(key); - msg.setId(uuid); - JSONObject json = new JSONObject(); - json.put("DeviceID", deviceId); - json.put("CmdType", "Broadcast"); - json.put("Result", "Failed"); - json.put("Description", String.format("语音广播操作失败,错误码: %s, %s", event.statusCode, event.msg)); - msg.setData(json); - resultHolder.invokeResult(msg); - }); + try { + cmder.audioBroadcastCmd(device, (event) -> { + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setId(uuid); + JSONObject json = new JSONObject(); + json.put("DeviceID", deviceId); + json.put("CmdType", "Broadcast"); + json.put("Result", "Failed"); + json.put("Description", String.format("语音广播操作失败,错误码: %s, %s", event.statusCode, event.msg)); + msg.setData(json); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 语音广播: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } result.onTimeout(() -> { - logger.warn(String.format("语音广播操作超时, 设备未返回应答指令")); + logger.warn("语音广播操作超时, 设备未返回应答指令"); RequestMessage msg = new RequestMessage(); msg.setKey(key); msg.setId(uuid); @@ -316,9 +308,9 @@ public class PlayController { return result; } - @ApiOperation("获取所有的ssrc") + @Operation(summary = "获取所有的ssrc") @GetMapping("/ssrc") - public WVPResult getSSRC() { + public JSONObject getSSRC() { if (logger.isDebugEnabled()) { logger.debug("获取所有的ssrc"); } @@ -329,18 +321,14 @@ public class PlayController { jsonObject.put("deviceId", transaction.getDeviceId()); jsonObject.put("channelId", transaction.getChannelId()); jsonObject.put("ssrc", transaction.getSsrc()); - jsonObject.put("streamId", transaction.getStreamId()); + jsonObject.put("streamId", transaction.getStream()); objects.add(jsonObject); } - WVPResult result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); JSONObject jsonObject = new JSONObject(); jsonObject.put("data", objects); jsonObject.put("count", objects.size()); - result.setData(jsonObject); - return result; + return jsonObject; } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/PlayResult.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/PlayResult.java index 42a6e8b3..58d4ce20 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/PlayResult.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/bean/PlayResult.java @@ -1,21 +1,23 @@ package com.genersoft.iot.vmp.vmanager.gb28181.play.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 { - private DeferredResult> result; + private DeferredResult> result; private String uuid; private Device device; - public DeferredResult> getResult() { + public DeferredResult> getResult() { return result; } - public void setResult(DeferredResult> result) { + public void setResult(DeferredResult> result) { this.result = result; } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java deleted file mode 100644 index 3f846c60..00000000 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/DownloadController.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.genersoft.iot.vmp.vmanager.gb28181.playback; - -import com.genersoft.iot.vmp.common.StreamInfo; -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.MediaServerItem; -import com.genersoft.iot.vmp.service.IMediaServerService; -import com.genersoft.iot.vmp.service.bean.SSRCInfo; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.service.IPlayService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import org.springframework.web.context.request.async.DeferredResult; - -import javax.sip.message.Response; -import java.util.UUID; - -@Api(tags = "历史媒体下载") -@CrossOrigin -@RestController -@RequestMapping("/api/download") -public class DownloadController { - - private final static Logger logger = LoggerFactory.getLogger(DownloadController.class); - - @Autowired - private SIPCommander cmder; - - @Autowired - private IVideoManagerStorager storager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; - - // @Autowired - // private ZLMRESTfulUtils zlmresTfulUtils; - - @Autowired - private IPlayService playService; - - @Autowired - private DeferredResultHolder resultHolder; - - @Autowired - private IMediaServerService mediaServerService; - - @ApiOperation("开始历史媒体下载") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "startTime", value = "开始时间", dataTypeClass = String.class), - @ApiImplicitParam(name = "endTime", value = "结束时间", dataTypeClass = String.class), - @ApiImplicitParam(name = "downloadSpeed", value = "下载倍速", dataTypeClass = String.class), - }) - @GetMapping("/start/{deviceId}/{channelId}") - public DeferredResult> play(@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)); - } - String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId; - String uuid = UUID.randomUUID().toString(); - DeferredResult> result = new DeferredResult>(30000L); - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData("Timeout"); - resultHolder.invokeAllResult(msg); - }); - if(resultHolder.exist(key, null)) { - return result; - } - resultHolder.put(key, uuid, result); - Device device = storager.queryVideoDevice(deviceId); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId); - if (streamInfo != null) { - // 停止之前的下载 - cmder.streamByeCmd(deviceId, channelId); - } - - MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); - if (newMediaServerItem == null) { - logger.warn(String.format("设备下载响应超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData("Timeout"); - resultHolder.invokeAllResult(msg); - return result; - } - - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true); - - cmder.downloadStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, (MediaServerItem mediaServerItem, JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForDownload(mediaServerItem, response, deviceId, channelId, uuid.toString()); - }, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeAllResult(msg); - }); - - return result; - } - - @ApiOperation("停止历史媒体下载") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - }) - @GetMapping("/stop/{deviceId}/{channelId}") - public ResponseEntity playStop(@PathVariable String deviceId, @PathVariable String channelId) { - - cmder.streamByeCmd(deviceId, channelId); - - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备历史媒体下载停止 API调用,deviceId/channelId:%s_%s", deviceId, channelId)); - } - - if (deviceId != null && channelId != null) { - JSONObject json = new JSONObject(); - json.put("deviceId", deviceId); - json.put("channelId", channelId); - return new ResponseEntity(json.toString(), HttpStatus.OK); - } else { - logger.warn("设备历史媒体下载停止API调用失败!"); - return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); - } - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java index 8f83e7c4..eb6f997d 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java @@ -1,38 +1,45 @@ package com.genersoft.iot.vmp.vmanager.gb28181.playback; 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.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; -//import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils; -import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; -import com.genersoft.iot.vmp.service.IMediaServerService; -import com.genersoft.iot.vmp.service.bean.SSRCInfo; +import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.service.IPlayService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +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 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.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.alibaba.fastjson.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import org.springframework.web.context.request.async.DeferredResult; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; import java.util.UUID; -@Api(tags = "视频回放") +/** + * @author lin + */ +@Tag(name = "视频回放") @CrossOrigin @RestController @RequestMapping("/api/playback") @@ -44,14 +51,14 @@ public class PlaybackController { private SIPCommander cmder; @Autowired - private IVideoManagerStorager storager; + private ZLMRTPServerFactory zlmrtpServerFactory; + + @Autowired + private IVideoManagerStorage storager; @Autowired private IRedisCatchStorage redisCatchStorage; - // @Autowired - // private ZLMRESTfulUtils zlmresTfulUtils; - @Autowired private IPlayService playService; @@ -59,184 +66,142 @@ public class PlaybackController { private DeferredResultHolder resultHolder; @Autowired - private IMediaServerService mediaServerService; + private UserSetting userSetting; - @ApiOperation("开始视频回放") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "startTime", value = "开始时间", dataTypeClass = String.class), - @ApiImplicitParam(name = "endTime", value = "结束时间", dataTypeClass = String.class), - }) + @Operation(summary = "开始视频回放") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) @GetMapping("/start/{deviceId}/{channelId}") - public DeferredResult> play(@PathVariable String deviceId, @PathVariable String channelId, - String startTime,String endTime) { + public DeferredResult> start(@PathVariable String deviceId, @PathVariable String channelId, + String startTime, String endTime) { if (logger.isDebugEnabled()) { logger.debug(String.format("设备回放 API调用,deviceId:%s ,channelId:%s", deviceId, channelId)); } + String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_PLAYBACK + deviceId + channelId; - DeferredResult> result = new DeferredResult>(30000L); - Device device = storager.queryVideoDevice(deviceId); - if (device == null) { - result.setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST)); - return result; - } - MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); - SSRCInfo ssrcInfo = mediaServerService.openRTPServer(newMediaServerItem, null, true); + DeferredResult> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue()); + resultHolder.put(key, uuid, result); - // 超时处理 - result.onTimeout(()->{ - logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - }); + WVPResult wvpResult = new WVPResult<>(); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByDevice(deviceId, channelId); - if (streamInfo != null) { - // 停止之前的回放 - cmder.streamByeCmd(deviceId, channelId); - } - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId, uuid, result); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + msg.setId(uuid); - if (newMediaServerItem == null) { - logger.warn(String.format("设备回放超时,deviceId:%s ,channelId:%s", deviceId, channelId)); - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData("Timeout"); - resultHolder.invokeResult(msg); - return result; - } - - cmder.playbackStreamCmd(newMediaServerItem, ssrcInfo, device, channelId, startTime, endTime, (MediaServerItem mediaServerItem, JSONObject response) -> { - logger.info("收到订阅消息: " + response.toJSONString()); - playService.onPublishHandlerForPlayBack(mediaServerItem, response, deviceId, channelId, uuid.toString()); - }, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("回放失败, 错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); + playService.playBack(deviceId, channelId, startTime, endTime, null, + playBackResult->{ + wvpResult.setCode(playBackResult.getCode()); + wvpResult.setMsg(playBackResult.getMsg()); + if (playBackResult.getCode() == ErrorCode.SUCCESS.getCode()) { + StreamInfo streamInfo = (StreamInfo)playBackResult.getData(); + wvpResult.setData(new StreamContent(streamInfo)); + } + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); return result; } - @ApiOperation("停止视频回放") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - }) - @GetMapping("/stop/{deviceId}/{channelId}") - public ResponseEntity playStop(@PathVariable String deviceId, @PathVariable String channelId) { - cmder.streamByeCmd(deviceId, channelId); - - if (logger.isDebugEnabled()) { - logger.debug(String.format("设备录像回放停止 API调用,deviceId/channelId:%s/%s", deviceId, channelId)); + @Operation(summary = "停止视频回放") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @GetMapping("/stop/{deviceId}/{channelId}/{stream}") + public void playStop( + @PathVariable String deviceId, + @PathVariable String channelId, + @PathVariable String stream) { + if (ObjectUtils.isEmpty(deviceId) || ObjectUtils.isEmpty(channelId) || ObjectUtils.isEmpty(stream)) { + throw new ControllerException(ErrorCode.ERROR400); } - - if (deviceId != null && channelId != null) { - JSONObject json = new JSONObject(); - json.put("deviceId", deviceId); - json.put("channelId", channelId); - return new ResponseEntity(json.toString(), HttpStatus.OK); - } else { - logger.warn("设备录像回放停止API调用失败!"); - return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + Device device = storager.queryVideoDevice(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()); } } - @ApiOperation("回放暂停") - @ApiImplicitParams({ - @ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class), - }) + + @Operation(summary = "回放暂停") + @Parameter(name = "streamId", description = "回放流ID", required = true) @GetMapping("/pause/{streamId}") - public ResponseEntity playPause(@PathVariable String streamId) { + public void playPause(@PathVariable String streamId) { logger.info("playPause: "+streamId); - JSONObject json = new JSONObject(); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); - if (null == streamInfo) { - json.put("msg", "streamId不存在"); - logger.warn("streamId不存在!"); - return new ResponseEntity(json.toString(), HttpStatus.BAD_REQUEST); + + try { + playService.pauseRtp(streamId); + } catch (ServiceException e) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), e.getMessage()); + } catch (InvalidArgumentException | ParseException | SipException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); } - Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); - cmder.playPauseCmd(device, streamInfo); - json.put("msg", "ok"); - return new ResponseEntity(json.toString(), HttpStatus.OK); } - @ApiOperation("回放恢复") - @ApiImplicitParams({ - @ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class), - }) + + @Operation(summary = "回放恢复") + @Parameter(name = "streamId", description = "回放流ID", required = true) @GetMapping("/resume/{streamId}") - public ResponseEntity playResume(@PathVariable String streamId) { + public void playResume(@PathVariable String streamId) { logger.info("playResume: "+streamId); - JSONObject json = new JSONObject(); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); - if (null == streamInfo) { - json.put("msg", "streamId不存在"); - logger.warn("streamId不存在!"); - return new ResponseEntity(json.toString(), HttpStatus.BAD_REQUEST); + try { + playService.resumeRtp(streamId); + } catch (ServiceException e) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), e.getMessage()); + } catch (InvalidArgumentException | ParseException | SipException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); } - Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); - cmder.playResumeCmd(device, streamInfo); - json.put("msg", "ok"); - return new ResponseEntity(json.toString(), HttpStatus.OK); } - @ApiOperation("回放拖动播放") - @ApiImplicitParams({ - @ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "seekTime", value = "拖动偏移量,单位s", dataTypeClass = Long.class), - }) + + @Operation(summary = "回放拖动播放") + @Parameter(name = "streamId", description = "回放流ID", required = true) + @Parameter(name = "seekTime", description = "拖动偏移量,单位s", required = true) @GetMapping("/seek/{streamId}/{seekTime}") - public ResponseEntity playSeek(@PathVariable String streamId, @PathVariable long seekTime) { + public void playSeek(@PathVariable String streamId, @PathVariable long seekTime) { logger.info("playSeek: "+streamId+", "+seekTime); - JSONObject json = new JSONObject(); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); if (null == streamInfo) { - json.put("msg", "streamId不存在"); logger.warn("streamId不存在!"); - return new ResponseEntity(json.toString(), HttpStatus.BAD_REQUEST); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); } Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); - cmder.playSeekCmd(device, streamInfo, seekTime); - json.put("msg", "ok"); - return new ResponseEntity(json.toString(), HttpStatus.OK); + try { + cmder.playSeekCmd(device, streamInfo, seekTime); + } catch (InvalidArgumentException | ParseException | SipException e) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); + } } - @ApiOperation("回放倍速播放") - @ApiImplicitParams({ - @ApiImplicitParam(name = "streamId", value = "回放流ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "speed", value = "倍速0.25 0.5 1、2、4", dataTypeClass = Double.class), - }) + @Operation(summary = "回放倍速播放") + @Parameter(name = "streamId", description = "回放流ID", required = true) + @Parameter(name = "speed", description = "倍速0.25 0.5 1、2、4", required = true) @GetMapping("/speed/{streamId}/{speed}") - public ResponseEntity playSpeed(@PathVariable String streamId, @PathVariable Double speed) { + public void playSpeed(@PathVariable String streamId, @PathVariable Double speed) { logger.info("playSpeed: "+streamId+", "+speed); - JSONObject json = new JSONObject(); - StreamInfo streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId); + StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null); if (null == streamInfo) { - json.put("msg", "streamId不存在"); logger.warn("streamId不存在!"); - return new ResponseEntity(json.toString(), HttpStatus.BAD_REQUEST); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "streamId不存在"); } if(speed != 0.25 && speed != 0.5 && speed != 1 && speed != 2.0 && speed != 4.0) { - json.put("msg", "不支持的speed(0.25 0.5 1、2、4)"); logger.warn("不支持的speed: " + speed); - return new ResponseEntity(json.toString(), HttpStatus.BAD_REQUEST); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "不支持的speed(0.25 0.5 1、2、4)"); } Device device = storager.queryVideoDevice(streamInfo.getDeviceID()); - cmder.playSpeedCmd(device, streamInfo, speed); - json.put("msg", "ok"); - return new ResponseEntity(json.toString(), HttpStatus.OK); + try { + cmder.playSpeedCmd(device, streamInfo, 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/vmanager/gb28181/ptz/PtzController.java index 68acde33..171f57ca 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java @@ -1,29 +1,31 @@ package com.genersoft.iot.vmp.vmanager.gb28181.ptz; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; + +import com.genersoft.iot.vmp.conf.exception.ControllerException; +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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; -import javax.sip.message.Response; - 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.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; import java.util.UUID; -@Api(tags = "云台控制") +@Tag(name = "云台控制") @CrossOrigin @RestController @RequestMapping("/api/ptz") @@ -35,7 +37,7 @@ public class PtzController { private SIPCommander cmder; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private DeferredResultHolder resultHolder; @@ -48,19 +50,17 @@ public class PtzController { * @param horizonSpeed 水平移动速度 * @param verticalSpeed 垂直移动速度 * @param zoomSpeed 缩放速度 - * @return String 控制结果 */ - @ApiOperation("云台控制") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "command", value = "控制指令,允许值: left, right, up, down, upleft, upright, downleft, downright, zoomin, zoomout, stop", dataTypeClass = String.class), - @ApiImplicitParam(name = "horizonSpeed", value = "水平速度", dataTypeClass = Integer.class), - @ApiImplicitParam(name = "verticalSpeed", value = "垂直速度", dataTypeClass = Integer.class), - @ApiImplicitParam(name = "zoomSpeed", value = "缩放速度", dataTypeClass = Integer.class), - }) + + @Operation(summary = "云台控制") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "command", description = "控制指令,允许值: left, right, up, down, upleft, upright, downleft, downright, zoomin, zoomout, stop", required = true) + @Parameter(name = "horizonSpeed", description = "水平速度", required = true) + @Parameter(name = "verticalSpeed", description = "垂直速度", required = true) + @Parameter(name = "zoomSpeed", description = "缩放速度", required = true) @PostMapping("/control/{deviceId}/{channelId}") - public ResponseEntity ptz(@PathVariable String deviceId,@PathVariable String channelId, String command, int horizonSpeed, int verticalSpeed, int zoomSpeed){ + 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)); @@ -99,50 +99,55 @@ public class PtzController { cmdCode = 32; break; case "stop": - cmdCode = 0; break; default: break; } - cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed); - return new ResponseEntity("success",HttpStatus.OK); + try { + cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 云台控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } } - @ApiOperation("通用前端控制命令") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "cmdCode", value = "指令码", dataTypeClass = Integer.class), - @ApiImplicitParam(name = "parameter1", value = "数据一", dataTypeClass = Integer.class), - @ApiImplicitParam(name = "parameter2", value = "数据二", dataTypeClass = Integer.class), - @ApiImplicitParam(name = "combindCode2", value = "组合码二", dataTypeClass = Integer.class), - }) + + @Operation(summary = "通用前端控制命令") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "cmdCode", description = "指令码", required = true) + @Parameter(name = "parameter1", description = "数据一", required = true) + @Parameter(name = "parameter2", description = "数据二", required = true) + @Parameter(name = "combindCode2", description = "组合码二", required = true) @PostMapping("/front_end_command/{deviceId}/{channelId}") - public ResponseEntity frontEndCommand(@PathVariable String deviceId,@PathVariable String channelId,int cmdCode, int parameter1, int parameter2, int combindCode2){ + 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)); } Device device = storager.queryVideoDevice(deviceId); - cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2); - return new ResponseEntity("success",HttpStatus.OK); + try { + cmder.frontEndCmd(device, channelId, cmdCode, parameter1, parameter2, combindCode2); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.error("[命令发送失败] 前端控制: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } } - @ApiOperation("预置位查询") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - }) + + @Operation(summary = "预置位查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) @GetMapping("/preset/query/{deviceId}/{channelId}") - public DeferredResult> presetQueryApi(@PathVariable String deviceId, @PathVariable String channelId) { + public DeferredResult presetQueryApi(@PathVariable String deviceId, @PathVariable String channelId) { if (logger.isDebugEnabled()) { logger.debug("设备预置位查询API调用"); } Device device = storager.queryVideoDevice(deviceId); String uuid = UUID.randomUUID().toString(); - String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (StringUtils.isEmpty(channelId) ? deviceId : channelId); - DeferredResult> result = new DeferredResult> (3 * 1000L); + String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + (ObjectUtils.isEmpty(channelId) ? deviceId : channelId); + DeferredResult result = new DeferredResult (3 * 1000L); result.onTimeout(()->{ logger.warn(String.format("获取设备预置位超时")); // 释放rtpserver @@ -152,18 +157,22 @@ public class PtzController { msg.setData("获取设备预置位超时"); resultHolder.invokeResult(msg); }); - resultHolder.put(key, uuid, result); if (resultHolder.exist(key, null)) { return result; } - cmder.presetQuery(device, channelId, event -> { - RequestMessage msg = new RequestMessage(); - msg.setId(uuid); - msg.setKey(key); - msg.setData(String.format("获取设备预置位失败,错误码: %s, %s", event.statusCode, event.msg)); - resultHolder.invokeResult(msg); - }); - + resultHolder.put(key, uuid, result); + try { + cmder.presetQuery(device, channelId, event -> { + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + msg.setData(String.format("获取设备预置位失败,错误码: %s, %s", event.statusCode, event.msg)); + resultHolder.invokeResult(msg); + }); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 获取设备预置位: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } return result; } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java index a8675e8e..c3b9c781 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java @@ -1,14 +1,22 @@ package com.genersoft.iot.vmp.vmanager.gb28181.record; +import com.genersoft.iot.vmp.common.StreamInfo; +import com.genersoft.iot.vmp.conf.exception.ControllerException; +import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.service.IPlayService; +import com.genersoft.iot.vmp.utils.DateUtil; +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 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.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -20,11 +28,14 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.RecordInfo; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; import java.util.UUID; -@Api(tags = "国标录像") +@Tag(name = "国标录像") @CrossOrigin @RestController @RequestMapping("/api/gb_record") @@ -36,45 +47,151 @@ public class GBRecordController { private SIPCommander cmder; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired private DeferredResultHolder resultHolder; - @ApiOperation("录像查询") - @ApiImplicitParams({ - @ApiImplicitParam(name = "deviceId", value = "设备ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "channelId", value = "通道ID", dataTypeClass = String.class), - @ApiImplicitParam(name = "startTime", value = "开始时间", dataTypeClass = String.class), - @ApiImplicitParam(name = "endTime", value = "结束时间", dataTypeClass = String.class), - }) + @Autowired + private IPlayService playService; + + @Autowired + private IDeviceService deviceService; + + + + + @Operation(summary = "录像查询") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) @GetMapping("/query/{deviceId}/{channelId}") - public DeferredResult> recordinfo(@PathVariable String deviceId,@PathVariable String channelId, String startTime, String endTime){ + 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, startTime:%s",deviceId, startTime, endTime)); + logger.debug(String.format("录像信息查询 API调用,deviceId:%s ,startTime:%s, endTime:%s",deviceId, startTime, endTime)); + } + DeferredResult> result = new DeferredResult<>(); + if (!DateUtil.verification(startTime, DateUtil.formatter)){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime error, format is " + DateUtil.PATTERN); + } + if (!DateUtil.verification(endTime, DateUtil.formatter)){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime error, format is " + DateUtil.PATTERN); } Device device = storager.queryVideoDevice(deviceId); // 指定超时时间 1分钟30秒 - DeferredResult> result = new DeferredResult<>(90*1000L); String uuid = UUID.randomUUID().toString(); int sn = (int)((Math.random()*9+1)*100000); String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn; RequestMessage msg = new RequestMessage(); msg.setId(uuid); msg.setKey(key); - cmder.recordInfoQuery(device, channelId, startTime, endTime, sn, (eventResult -> { - msg.setData("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg ); - resultHolder.invokeResult(msg); - })); + try { + cmder.recordInfoQuery(device, channelId, startTime, endTime, sn, null, null, null, (eventResult -> { + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("查询录像失败, status: " + eventResult.statusCode + ", message: " + eventResult.msg); + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + })); + } catch (InvalidArgumentException | SipException | ParseException e) { + logger.error("[命令发送失败] 查询录像: {}", e.getMessage()); + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); + } // 录像查询以channelId作为deviceId查询 resultHolder.put(key, uuid, result); result.onTimeout(()->{ msg.setData("timeout"); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(ErrorCode.ERROR100.getCode()); + wvpResult.setMsg("timeout"); + msg.setData(wvpResult); resultHolder.invokeResult(msg); }); return result; } + + + @Operation(summary = "开始历史媒体下载") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + @Parameter(name = "downloadSpeed", description = "下载倍速", required = true) + @GetMapping("/download/start/{deviceId}/{channelId}") + public DeferredResult> download(@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)); + } + + String uuid = UUID.randomUUID().toString(); + String key = DeferredResultHolder.CALLBACK_CMD_DOWNLOAD + deviceId + channelId; + DeferredResult> result = new DeferredResult<>(30000L); + resultHolder.put(key, uuid, result); + RequestMessage msg = new RequestMessage(); + msg.setId(uuid); + msg.setKey(key); + + WVPResult wvpResult = new WVPResult<>(); + + playService.download(deviceId, channelId, startTime, endTime, Integer.parseInt(downloadSpeed), null, playBackResult->{ + + wvpResult.setCode(playBackResult.getCode()); + wvpResult.setMsg(playBackResult.getMsg()); + if (playBackResult.getCode() == ErrorCode.SUCCESS.getCode()) { + StreamInfo streamInfo = (StreamInfo)playBackResult.getData(); + wvpResult.setData(new StreamContent(streamInfo)); + } + msg.setData(wvpResult); + resultHolder.invokeResult(msg); + }); + + return result; + } + + @Operation(summary = "停止历史媒体下载") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @Parameter(name = "stream", description = "流ID", required = true) + @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 (deviceId == null || channelId == null) { + throw new ControllerException(ErrorCode.ERROR400); + } + + Device device = deviceService.getDevice(deviceId); + if (device == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "设备:" + deviceId + "未找到"); + } + + 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()); + } + } + + @Operation(summary = "获取历史媒体下载进度") + @Parameter(name = "deviceId", description = "设备国标编号", required = true) + @Parameter(name = "channelId", description = "通道国标编号", required = true) + @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); + if (downLoadInfo == null) { + throw new ControllerException(ErrorCode.ERROR404); + } + return new StreamContent(downLoadInfo); + } } 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 650dbe03..42efbb1a 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java @@ -1,27 +1,29 @@ package com.genersoft.iot.vmp.vmanager.log; -import com.genersoft.iot.vmp.conf.UserSetup; -import com.genersoft.iot.vmp.media.zlm.ZLMRunner; +import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.service.ILogService; import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +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.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; + +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.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import java.text.ParseException; -import java.text.SimpleDateFormat; -@Api(tags = "日志管理") +@Tag(name = "日志管理") @CrossOrigin @RestController @RequestMapping("/api/log") @@ -33,9 +35,7 @@ public class LogController { private ILogService logService; @Autowired - private UserSetup userSetup; - - private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private UserSetting userSetting; /** * 分页查询日志 @@ -48,17 +48,15 @@ public class LogController { * @param endTime 结束时间 * @return */ - @ApiOperation("分页查询报警") @GetMapping("/all") - @ApiImplicitParams({ - @ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class), - @ApiImplicitParam(name="page", value = "当前页", required = true ,dataTypeClass = Integer.class), - @ApiImplicitParam(name="count", value = "每页查询数量", required = true ,dataTypeClass = Integer.class), - @ApiImplicitParam(name="type", value = "类型" ,dataTypeClass = String.class), - @ApiImplicitParam(name="startTime", value = "查询内容" ,dataTypeClass = String.class), - @ApiImplicitParam(name="endTime", value = "查询内容" ,dataTypeClass = String.class), - }) - public ResponseEntity> getAll( + @Operation(summary = "分页查询报警") + @Parameter(name = "query", description = "查询内容", required = true) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @Parameter(name = "type", description = "类型", required = true) + @Parameter(name = "startTime", description = "开始时间", required = true) + @Parameter(name = "endTime", description = "结束时间", required = true) + public PageInfo getAll( @RequestParam int page, @RequestParam int count, @RequestParam(required = false) String query, @@ -66,39 +64,34 @@ public class LogController { @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime ) { - if (StringUtils.isEmpty(query)) query = null; - if (StringUtils.isEmpty(startTime)) startTime = null; - if (StringUtils.isEmpty(endTime)) endTime = null; - if (!userSetup.getLogInDatebase()) { + if (ObjectUtils.isEmpty(query)) { + query = null; + } + if (ObjectUtils.isEmpty(startTime)) { + startTime = null; + } + if (ObjectUtils.isEmpty(endTime)) { + endTime = null; + } + if (!userSetting.getLogInDatebase()) { logger.warn("自动记录日志功能已关闭,查询结果可能不完整。"); } - try { - if (startTime != null) format.parse(startTime); - if (endTime != null) format.parse(endTime); - } catch (ParseException e) { - return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); + if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){ + throw new ControllerException(ErrorCode.ERROR400); } - PageInfo allLog = logService.getAll(page, count, query, type, startTime, endTime); - return new ResponseEntity<>(allLog, HttpStatus.OK); + return logService.getAll(page, count, query, type, startTime, endTime); } /** * 清空日志 * */ - @ApiOperation("清空日志") + @Operation(summary = "停止视频回放") @DeleteMapping("/clear") - @ApiImplicitParams({}) - public ResponseEntity> clear() { - - int count = logService.clear(); - WVPResult wvpResult = new WVPResult(); - wvpResult.setCode(0); - wvpResult.setMsg("success"); - wvpResult.setData(count); - return new ResponseEntity>(wvpResult, HttpStatus.OK); + public void clear() { + logService.clear(); } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java index 844c9b70..ec88b507 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/record/RecordController.java @@ -1,6 +1,6 @@ //package com.genersoft.iot.vmp.vmanager.record; // -//import com.alibaba.fastjson.JSONObject; +//import com.alibaba.fastjson2.JSONObject; //import com.genersoft.iot.vmp.media.zlm.dto.StreamPushItem; //import com.genersoft.iot.vmp.service.IRecordInfoServer; //import com.genersoft.iot.vmp.storager.dao.dto.RecordInfo; @@ -13,7 +13,7 @@ //import org.springframework.beans.factory.annotation.Autowired; //import org.springframework.web.bind.annotation.*; // -//@Api(tags = "云端录像") +//@Tag(name = "云端录像") //@CrossOrigin //@RestController //@RequestMapping("/api/record") @@ -22,7 +22,7 @@ // @Autowired // private IRecordInfoServer recordInfoServer; // -// @ApiOperation("录像列表查询") +// //@ApiOperation("录像列表查询") // @ApiImplicitParams({ // @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class), // @ApiImplicitParam(name="count", value = "每页查询数量", required = true, dataTypeClass = Integer.class), @@ -37,7 +37,7 @@ // return recordList; // } // -// @ApiOperation("获取录像详情") +// //@ApiOperation("获取录像详情") // @ApiImplicitParams({ // @ApiImplicitParam(name="recordInfo", value = "录像记录", required = true, dataTypeClass = RecordInfo.class) // }) 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 4e223e98..6b8550db 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java @@ -1,252 +1,275 @@ package com.genersoft.iot.vmp.vmanager.server; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.VManageBootstrap; +import com.genersoft.iot.vmp.common.SystemAllInfo; import com.genersoft.iot.vmp.common.VersionPo; import com.genersoft.iot.vmp.conf.SipConfig; -import com.genersoft.iot.vmp.conf.UserSetup; +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.media.zlm.ZlmHttpHookSubscribe; +import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; -import com.genersoft.iot.vmp.service.IMediaServerService; +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.utils.SpringBeanFactory; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo; +import com.genersoft.iot.vmp.vmanager.bean.ResourceInfo; +import com.genersoft.iot.vmp.vmanager.bean.SystemConfigInfo; import gov.nist.javax.sip.SipStackImpl; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.util.StringUtils; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import javax.sip.ListeningPoint; import javax.sip.ObjectInUseException; import javax.sip.SipProvider; -import java.util.Iterator; -import java.util.List; +import java.util.*; @SuppressWarnings("rawtypes") -@Api(tags = "服务控制") +@Tag(name = "服务控制") @CrossOrigin @RestController @RequestMapping("/api/server") public class ServerController { @Autowired - private ConfigurableApplicationContext context; + private ZlmHttpHookSubscribe zlmHttpHookSubscribe; @Autowired private IMediaServerService mediaServerService; @Autowired - VersionInfo versionInfo; + private VersionInfo versionInfo; @Autowired - SipConfig sipConfig; + private SipConfig sipConfig; @Autowired - UserSetup userSetup; + private UserSetting userSetting; + + @Autowired + private IDeviceService deviceService; + + @Autowired + private IDeviceChannelService channelService; + + @Autowired + private IStreamPushService pushService; + + + @Autowired + private IStreamProxyService proxyService; + @Value("${server.port}") private int serverPort; - @ApiOperation("流媒体服务列表") + @Autowired + private ThreadPoolTaskExecutor taskExecutor; + + @Autowired + private IRedisCatchStorage redisCatchStorage; + + @GetMapping(value = "/media_server/list") @ResponseBody - public WVPResult> getMediaServerList(boolean detail){ - List all = mediaServerService.getAll(); - - WVPResult> result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - result.setData(mediaServerService.getAll()); - return result; + @Operation(summary = "流媒体服务列表") + public List getMediaServerList() { + return mediaServerService.getAll(); } - @ApiOperation("在线流媒体服务列表") @GetMapping(value = "/media_server/online/list") @ResponseBody - public WVPResult> getOnlineMediaServerList(){ - WVPResult> result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - result.setData(mediaServerService.getAllOnline()); - return result; + @Operation(summary = "在线流媒体服务列表") + public List getOnlineMediaServerList() { + return mediaServerService.getAllOnline(); } - @ApiOperation("获取流媒体服务") @GetMapping(value = "/media_server/one/{id}") @ResponseBody - public WVPResult getMediaServer(@PathVariable String id){ - WVPResult result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - result.setData(mediaServerService.getOne(id)); - return result; + @Operation(summary = "停止视频回放") + @Parameter(name = "id", description = "流媒体服务ID", required = true) + public MediaServerItem getMediaServer(@PathVariable String id) { + return mediaServerService.getOne(id); } - @ApiOperation("测试流媒体服务") - @ApiImplicitParams({ - @ApiImplicitParam(name="ip", value = "流媒体服务IP", dataTypeClass = String.class), - @ApiImplicitParam(name="port", value = "流媒体服务HTT端口", dataTypeClass = Integer.class), - @ApiImplicitParam(name="secret", value = "流媒体服务secret", dataTypeClass = String.class), - }) + @Operation(summary = "测试流媒体服务") + @Parameter(name = "ip", description = "流媒体服务IP", required = true) + @Parameter(name = "port", description = "流媒体服务HTT端口", required = true) + @Parameter(name = "secret", description = "流媒体服务secret", required = true) @GetMapping(value = "/media_server/check") @ResponseBody - public WVPResult checkMediaServer(@RequestParam String ip, @RequestParam int port, @RequestParam String secret){ + public MediaServerItem checkMediaServer(@RequestParam String ip, @RequestParam int port, @RequestParam String secret) { return mediaServerService.checkMediaServer(ip, port, secret); } - @ApiOperation("测试流媒体录像管理服务") - @ApiImplicitParams({ - @ApiImplicitParam(name="ip", value = "流媒体服务IP", dataTypeClass = String.class), - @ApiImplicitParam(name="port", value = "流媒体服务HTT端口", dataTypeClass = Integer.class), - @ApiImplicitParam(name="secret", value = "流媒体服务secret", dataTypeClass = String.class), - }) + @Operation(summary = "测试流媒体录像管理服务") + @Parameter(name = "ip", description = "流媒体服务IP", required = true) + @Parameter(name = "port", description = "流媒体服务HTT端口", required = true) @GetMapping(value = "/media_server/record/check") @ResponseBody - public WVPResult checkMediaRecordServer(@RequestParam String ip, @RequestParam int port){ + public void checkMediaRecordServer(@RequestParam String ip, @RequestParam int port) { boolean checkResult = mediaServerService.checkMediaRecordServer(ip, port); - WVPResult result = new WVPResult<>(); - if (checkResult) { - result.setCode(0); - result.setMsg("success"); - - }else { - result.setCode(-1); - result.setMsg("连接失败"); + if (!checkResult) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接失败"); } - return result; } - @ApiOperation("保存流媒体服务") - @ApiImplicitParams({ - @ApiImplicitParam(name="mediaServerItem", value = "流媒体信息", dataTypeClass = MediaServerItem.class) - }) + @Operation(summary = "保存流媒体服务") + @Parameter(name = "mediaServerItem", description = "流媒体信息", required = true) @PostMapping(value = "/media_server/save") @ResponseBody - public WVPResult saveMediaServer(@RequestBody MediaServerItem mediaServerItem){ + public void saveMediaServer(@RequestBody MediaServerItem mediaServerItem) { MediaServerItem mediaServerItemInDatabase = mediaServerService.getOne(mediaServerItem.getId()); if (mediaServerItemInDatabase != null) { - if (StringUtils.isEmpty(mediaServerItemInDatabase.getSendRtpPortRange()) - && StringUtils.isEmpty(mediaServerItem.getSendRtpPortRange())){ - mediaServerItem.setSendRtpPortRange("30000,30500"); - } - mediaServerService.update(mediaServerItem); - }else { - if (StringUtils.isEmpty(mediaServerItem.getSendRtpPortRange())){ - mediaServerItem.setSendRtpPortRange("30000,30500"); - } - return mediaServerService.add(mediaServerItem); + mediaServerService.update(mediaServerItem); + } else { + mediaServerService.add(mediaServerItem); } - - WVPResult result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - return result; } - @ApiOperation("移除流媒体服务") - @ApiImplicitParams({ - @ApiImplicitParam(name="id", value = "流媒体ID", dataTypeClass = String.class) - }) + @Operation(summary = "移除流媒体服务") + @Parameter(name = "id", description = "流媒体ID", required = true) @DeleteMapping(value = "/media_server/delete") @ResponseBody - public WVPResult deleteMediaServer(@RequestParam String id){ - if (mediaServerService.getOne(id) != null) { - mediaServerService.delete(id); - }else { - WVPResult result = new WVPResult<>(); - result.setCode(-1); - result.setMsg("未找到此节点"); - return result; + public void deleteMediaServer(@RequestParam String id) { + if (mediaServerService.getOne(id) == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到此节点"); } - WVPResult result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - return result; + mediaServerService.delete(id); + mediaServerService.deleteDb(id); } - - @ApiOperation("重启服务") + @Operation(summary = "重启服务") @GetMapping(value = "/restart") @ResponseBody - public Object restart(){ - Thread restartThread = new Thread(new Runnable() { - @Override - public void run() { - 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 ignored) { - } catch (ObjectInUseException e) { - e.printStackTrace(); - } - } - }); + public void restart() { +// 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) { +// throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); +// } +// }); + }; - restartThread.setDaemon(false); - restartThread.start(); - return "success"; + @Operation(summary = "获取系统信息信息") + @GetMapping(value = "/system/configInfo") + @ResponseBody + public SystemConfigInfo getConfigInfo() { + SystemConfigInfo systemConfigInfo = new SystemConfigInfo(); + systemConfigInfo.setVersion(versionInfo.getVersion()); + systemConfigInfo.setSip(sipConfig); + systemConfigInfo.setAddOn(userSetting); + systemConfigInfo.setServerPort(serverPort); + return systemConfigInfo; } - @ApiOperation("版本信息") + @Operation(summary = "获取版本信息") @GetMapping(value = "/version") @ResponseBody - public WVPResult getVersion(){ - WVPResult result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - result.setData(versionInfo.getVersion()); - return result; + public VersionPo VersionPogetVersion() { + return versionInfo.getVersion(); } - @ApiOperation("配置信息") @GetMapping(value = "/config") - @ApiImplicitParams({ - @ApiImplicitParam(name="type", value = "配置类型(sip, base)", dataTypeClass = String.class), - }) + @Operation(summary = "获取配置信息") + @Parameter(name = "type", description = "配置类型(sip, base)", required = true) @ResponseBody - public WVPResult getVersion(String type){ - WVPResult result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); + public JSONObject getVersion(String type) { JSONObject jsonObject = new JSONObject(); jsonObject.put("server.port", serverPort); - if (StringUtils.isEmpty(type)) { + if (ObjectUtils.isEmpty(type)) { jsonObject.put("sip", JSON.toJSON(sipConfig)); - jsonObject.put("base", JSON.toJSON(userSetup)); - }else { - switch (type){ + jsonObject.put("base", JSON.toJSON(userSetting)); + } else { + switch (type) { case "sip": jsonObject.put("sip", sipConfig); break; case "base": - jsonObject.put("base", userSetup); + jsonObject.put("base", userSetting); break; default: break; } } - result.setData(jsonObject); + return jsonObject; + } + + @GetMapping(value = "/hooks") + @ResponseBody + @Operation(summary = "获取当前所有hook") + public List getHooks() { + return zlmHttpHookSubscribe.getAll(); + } + + @GetMapping(value = "/system/info") + @ResponseBody + @Operation(summary = "获取系统信息") + public SystemAllInfo getSystemInfo() { + SystemAllInfo systemAllInfo = redisCatchStorage.getSystemInfo(); + + return systemAllInfo; + } + + @GetMapping(value = "/media_server/load") + @ResponseBody + @Operation(summary = "获取负载信息") + public List getMediaLoad() { + List result = new ArrayList<>(); + List allOnline = mediaServerService.getAllOnline(); + if (allOnline.size() == 0) { + return result; + }else { + for (MediaServerItem mediaServerItem : allOnline) { + result.add(mediaServerService.getLoad(mediaServerItem)); + } + } + return result; + } + + @GetMapping(value = "/resource/info") + @ResponseBody + @Operation(summary = "获取负载信息") + public ResourceInfo getResourceInfo() { + ResourceInfo result = new ResourceInfo(); + ResourceBaceInfo deviceInfo = deviceService.getOverview(); + result.setDevice(deviceInfo); + ResourceBaceInfo channelInfo = channelService.getOverview(); + result.setChannel(channelInfo); + ResourceBaceInfo pushInfo = pushService.getOverview(); + result.setPush(pushInfo); + ResourceBaceInfo proxyInfo = proxyService.getOverview(); + result.setProxy(proxyInfo); + return result; } } 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 index 0c4c2ff7..65ec3d33 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/streamProxy/StreamProxyController.java @@ -1,31 +1,29 @@ package com.genersoft.iot.vmp.vmanager.streamProxy; -import com.alibaba.fastjson.JSONObject; -import com.genersoft.iot.vmp.common.StreamInfo; +import com.alibaba.fastjson2.JSONObject; +import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem; import com.genersoft.iot.vmp.service.IMediaServerService; -import com.genersoft.iot.vmp.service.IMediaService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.service.IStreamProxyService; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.StreamContent; import com.github.pagehelper.PageInfo; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +import 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.stereotype.Controller; -import org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; @SuppressWarnings("rawtypes") /** * 拉流代理接口 */ -@Api(tags = "拉流代理") +@Tag(name = "拉流代理", description = "") @Controller @CrossOrigin @RequestMapping(value = "/api/proxy") @@ -33,10 +31,6 @@ public class StreamProxyController { private final static Logger logger = LoggerFactory.getLogger(StreamProxyController.class); - @Autowired - private IRedisCatchStorage redisCatchStorage; - - @Autowired private IMediaServerService mediaServerService; @@ -44,13 +38,11 @@ public class StreamProxyController { private IStreamProxyService streamProxyService; - @ApiOperation("分页查询流代理") - @ApiImplicitParams({ - @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class), - @ApiImplicitParam(name="count", value = "每页查询数量", required = true, dataTypeClass = Integer.class), - @ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class), - @ApiImplicitParam(name="online", value = "是否在线", dataTypeClass = Boolean.class), - }) + @Operation(summary = "分页查询流代理") + @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, @@ -61,82 +53,77 @@ public class StreamProxyController { return streamProxyService.getAll(page, count); } - @ApiOperation("保存代理") - @ApiImplicitParams({ - @ApiImplicitParam(name = "param", value = "代理参数", dataTypeClass = StreamProxyItem.class), + @Operation(summary = "保存代理", parameters = { + @Parameter(name = "param", description = "代理参数", required = true), }) @PostMapping(value = "/save") @ResponseBody - public WVPResult save(@RequestBody StreamProxyItem param){ + public StreamContent save(@RequestBody StreamProxyItem param){ logger.info("添加代理: " + JSONObject.toJSONString(param)); - if (StringUtils.isEmpty(param.getMediaServerId())) param.setMediaServerId("auto"); - if (StringUtils.isEmpty(param.getType())) param.setType("default"); - WVPResult result = streamProxyService.save(param); - return result; + if (ObjectUtils.isEmpty(param.getMediaServerId())) { + param.setMediaServerId("auto"); + } + if (ObjectUtils.isEmpty(param.getType())) { + param.setType("default"); + } + if (ObjectUtils.isEmpty(param.getGbId())) { + param.setGbId(null); + } + return new StreamContent(streamProxyService.save(param)); } - @ApiOperation("获取ffmpeg.cmd模板") @GetMapping(value = "/ffmpeg_cmd/list") - @ApiImplicitParams({ - @ApiImplicitParam(name = "mediaServerId", value = "流媒体ID", dataTypeClass = String.class), - }) @ResponseBody - public WVPResult getFFmpegCMDs(@RequestParam String mediaServerId){ + @Operation(summary = "获取ffmpeg.cmd模板") + @Parameter(name = "mediaServerId", description = "流媒体ID", required = true) + public JSONObject getFFmpegCMDs(@RequestParam String mediaServerId){ logger.debug("获取节点[ {} ]ffmpeg.cmd模板", mediaServerId ); MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId); - JSONObject data = streamProxyService.getFFmpegCMDs(mediaServerItem); - WVPResult result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - result.setData(data); - return result; + if (mediaServerItem == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "流媒体: " + mediaServerId + "未找到"); + } + return streamProxyService.getFFmpegCMDs(mediaServerItem); } - @ApiOperation("移除代理") - @ApiImplicitParams({ - @ApiImplicitParam(name = "app", value = "应用名", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "stream", value = "流ID", required = true, dataTypeClass = String.class), - }) @DeleteMapping(value = "/del") @ResponseBody - public WVPResult del(@RequestParam String app, @RequestParam String stream){ + @Operation(summary = "移除代理") + @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); - WVPResult result = new WVPResult<>(); if (app == null || stream == null) { - result.setCode(400); - result.setMsg(app == null ?"app不能为null":"stream不能为null"); + throw new ControllerException(ErrorCode.ERROR400.getCode(), app == null ?"app不能为null":"stream不能为null"); }else { streamProxyService.del(app, stream); - result.setCode(0); - result.setMsg("success"); } - return result; } - @ApiOperation("启用代理") - @ApiImplicitParams({ - @ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class), - @ApiImplicitParam(name = "stream", value = "流ID", dataTypeClass = String.class), - }) @GetMapping(value = "/start") @ResponseBody - public Object start(String app, String stream){ + @Operation(summary = "启用代理") + @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); - return result?"success":"fail"; + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } } - @ApiOperation("停用代理") - @ApiImplicitParams({ - @ApiImplicitParam(name = "app", value = "应用名", dataTypeClass = String.class), - @ApiImplicitParam(name = "stream", value = "流ID", dataTypeClass = String.class), - }) @GetMapping(value = "/stop") @ResponseBody - public Object stop(String app, String stream){ + @Operation(summary = "停用代理") + @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); - return result?"success":"fail"; + if (!result) { + logger.info("停用代理失败: " + app + "/" + stream); + throw new ControllerException(ErrorCode.ERROR100); + } } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java index 6dfc2575..7506433f 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/streamPush/StreamPushController.java @@ -1,21 +1,45 @@ package com.genersoft.iot.vmp.vmanager.streamPush; +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.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.service.IMediaServerService; +import com.genersoft.iot.vmp.service.IMediaService; import com.genersoft.iot.vmp.service.IStreamPushService; -import com.genersoft.iot.vmp.vmanager.bean.WVPResult; +import com.genersoft.iot.vmp.service.impl.StreamPushUploadFileHandler; +import com.genersoft.iot.vmp.vmanager.bean.*; import com.github.pagehelper.PageInfo; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +import 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.http.HttpStatus; +import org.springframework.http.ResponseEntity; 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 org.springframework.web.multipart.MultipartFile; -@Api(tags = "推流信息管理") +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Tag(name = "推流信息管理") @Controller @CrossOrigin @RequestMapping(value = "/api/push") @@ -26,67 +50,231 @@ public class StreamPushController { @Autowired private IStreamPushService streamPushService; - @ApiOperation("推流列表查询") - @ApiImplicitParams({ - @ApiImplicitParam(name="page", value = "当前页", required = true, dataTypeClass = Integer.class), - @ApiImplicitParam(name="count", value = "每页查询数量", required = true, dataTypeClass = Integer.class), - @ApiImplicitParam(name="query", value = "查询内容", dataTypeClass = String.class), - @ApiImplicitParam(name="online", value = "是否在线", dataTypeClass = Boolean.class), - }) + @Autowired + private IMediaServerService mediaServerService; + + @Autowired + private DeferredResultHolder resultHolder; + + @Autowired + private IMediaService mediaService; + + @Autowired + private UserSetting userSetting; + @GetMapping(value = "/list") @ResponseBody + @Operation(summary = "推流列表查询") + @Parameter(name = "page", description = "当前页") + @Parameter(name = "count", description = "每页查询数量") + @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 online ){ + @RequestParam(required = false)Boolean pushing, + @RequestParam(required = false)String mediaServerId ){ - PageInfo pushList = streamPushService.getPushList(page, count); + if (ObjectUtils.isEmpty(query)) { + query = null; + } + if (ObjectUtils.isEmpty(mediaServerId)) { + mediaServerId = null; + } + PageInfo pushList = streamPushService.getPushList(page, count, query, pushing, mediaServerId); return pushList; } - @ApiOperation("将推流添加到国标") - @ApiImplicitParams({ - @ApiImplicitParam(name = "stream", value = "直播流关联国标平台", dataTypeClass = GbStream.class), - }) @PostMapping(value = "/save_to_gb") @ResponseBody - public Object saveToGB(@RequestBody GbStream stream){ - if (streamPushService.saveToGB(stream)){ - return "success"; - }else { - return "fail"; + @Operation(summary = "将推流添加到国标") + public void saveToGB(@RequestBody GbStream stream){ + if (!streamPushService.saveToGB(stream)){ + throw new ControllerException(ErrorCode.ERROR100); } } - @ApiOperation("将推流移出到国标") - @ApiImplicitParams({ - @ApiImplicitParam(name = "stream", value = "直播流关联国标平台", dataTypeClass = GbStream.class), - }) @DeleteMapping(value = "/remove_form_gb") @ResponseBody - public Object removeFormGB(@RequestBody GbStream stream){ - if (streamPushService.removeFromGB(stream)){ - return "success"; - }else { - return "fail"; + @Operation(summary = "将推流移出到国标") + public void removeFormGB(@RequestBody GbStream stream){ + if (!streamPushService.removeFromGB(stream)){ + throw new ControllerException(ErrorCode.ERROR100); } } - @ApiOperation("中止一个推流") - @ApiImplicitParams({ - @ApiImplicitParam(name = "app", value = "应用名", required = true, dataTypeClass = String.class), - @ApiImplicitParam(name = "streamId", value = "流ID", required = true, dataTypeClass = String.class), - }) @PostMapping(value = "/stop") @ResponseBody - public Object removeFormGB(@RequestParam(required = true)String app, @RequestParam(required = true)String streamId){ - if (streamPushService.stop(app, streamId)){ - return "success"; - }else { - return "fail"; + @Operation(summary = "中止一个推流") + @Parameter(name = "app", description = "应用名", required = true) + @Parameter(name = "stream", description = "流id", required = true) + public void stop(String app, String streamId){ + if (!streamPushService.stop(app, streamId)){ + throw new ControllerException(ErrorCode.ERROR100); } } + @DeleteMapping(value = "/batchStop") + @ResponseBody + @Operation(summary = "中止多个推流") + public void batchStop(@RequestBody BatchGBStreamParam batchGBStreamParam){ + if (batchGBStreamParam.getGbStreams().size() == 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + if (!streamPushService.batchStop(batchGBStreamParam.getGbStreams())){ + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @PostMapping(value = "upload") + @ResponseBody + public DeferredResult>> uploadChannelFile(@RequestParam(value = "file") MultipartFile file){ + + // 最多处理文件一个小时 + DeferredResult>> result = new DeferredResult<>(60*60*1000L); + // 录像查询以channelId作为deviceId查询 + String key = DeferredResultHolder.UPLOAD_FILE_CHANNEL; + String uuid = UUID.randomUUID().toString(); + logger.info("通道导入文件类型: {}",file.getContentType() ); + if (file.isEmpty()) { + logger.warn("通道导入文件为空"); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("文件为空"); + result.setResult(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(wvpResult)); + return result; + } + if (file.getContentType() == null) { + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("无法识别文件类型"); + result.setResult(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(wvpResult)); + return result; + } + // 同时只处理一个文件 + if (resultHolder.exist(key, null)) { + logger.warn("已有导入任务正在执行"); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("已有导入任务正在执行"); + result.setResult(ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(wvpResult)); + return result; + } + + resultHolder.put(key, uuid, result); + result.onTimeout(()->{ + logger.warn("通道导入超时,可能文件过大"); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("导入超时,可能文件过大"); + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + }); + //获取文件流 + InputStream inputStream = null; + try { + String name = file.getName(); + inputStream = file.getInputStream(); + } catch (IOException e) { + e.printStackTrace(); + } + 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()); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + WVPResult>> wvpResult = new WVPResult<>(); + if (errorStreams.size() == 0 && errorGBs.size() == 0) { + wvpResult.setCode(0); + wvpResult.setMsg("成功"); + }else { + wvpResult.setCode(1); + wvpResult.setMsg("导入成功。但是存在重复数据"); + Map> errorData = new HashMap<>(); + errorData.put("gbId", errorGBs); + errorData.put("stream", errorStreams); + wvpResult.setData(errorData); + } + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + })).build(); + ReadSheet readSheet = EasyExcel.readSheet(0).build(); + excelReader.read(readSheet); + excelReader.finish(); + }catch (Exception e) { + logger.warn("通道导入失败:", e); + RequestMessage msg = new RequestMessage(); + msg.setKey(key); + WVPResult wvpResult = new WVPResult<>(); + wvpResult.setCode(-1); + wvpResult.setMsg("通道导入失败: " + e.getMessage() ); + msg.setData(wvpResult); + resultHolder.invokeAllResult(msg); + } + + + return result; + } + + /** + * 获取推流播放地址 + * @param app 应用名 + * @param stream 流id + * @return + */ + @GetMapping(value = "/getPlayUrl") + @ResponseBody + @Operation(summary = "获取推流播放地址") + @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 = mediaService.getStreamInfoByAppAndStreamWithCheck(app, stream, mediaServerId, authority); + if (streamInfo == null){ + throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取播放地址失败"); + } + return new StreamContent(streamInfo); + } + + /** + * 添加推流信息 + * @param stream 推流信息 + * @return + */ + @PostMapping(value = "/add") + @ResponseBody + @Operation(summary = "添加推流信息") + public void add(@RequestBody StreamPushItem 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"); + if (!streamPushService.add(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 6a75e1dd..769dca00 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/user/RoleController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/RoleController.java @@ -1,27 +1,24 @@ package com.genersoft.iot.vmp.vmanager.user; +import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.SecurityUtils; import com.genersoft.iot.vmp.service.IRoleService; -import com.genersoft.iot.vmp.service.IUserService; import com.genersoft.iot.vmp.storager.dao.dto.Role; -import com.genersoft.iot.vmp.storager.dao.dto.User; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.util.DigestUtils; -import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; -import java.text.SimpleDateFormat; import java.util.List; -@Api(tags = "角色管理") +@Tag(name = "角色管理") @CrossOrigin @RestController @RequestMapping("/api/role") @@ -30,72 +27,53 @@ public class RoleController { @Autowired private IRoleService roleService; - private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - @ApiOperation("添加角色") - @ApiImplicitParams({ - @ApiImplicitParam(name = "name", required = true, value = "角色名", dataTypeClass = String.class), - @ApiImplicitParam(name = "authority", required = true, value = "权限(自行定义内容,目前未使用)", dataTypeClass = String.class), - }) @PostMapping("/add") - public ResponseEntity> add(@RequestParam String name, + @Operation(summary = "添加角色") + @Parameter(name = "name", description = "角色名", required = true) + @Parameter(name = "authority", description = "权限(自行定义内容,目前未使用)", required = true) + public void add(@RequestParam String name, @RequestParam(required = false) String authority){ - WVPResult result = new WVPResult<>(); // 获取当前登录用户id int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); if (currenRoleId != 1) { // 只用角色id为1才可以删除和添加用户 - result.setCode(-1); - result.setMsg("用户无权限"); - return new ResponseEntity<>(result, HttpStatus.FORBIDDEN); + throw new ControllerException(ErrorCode.ERROR403); } Role role = new Role(); role.setName(name); role.setAuthority(authority); - role.setCreateTime(format.format(System.currentTimeMillis())); - role.setUpdateTime(format.format(System.currentTimeMillis())); + role.setCreateTime(DateUtil.getNow()); + role.setUpdateTime(DateUtil.getNow()); int addResult = roleService.add(role); - - result.setCode(addResult > 0 ? 0 : -1); - result.setMsg(addResult > 0 ? "success" : "fail"); - result.setData(addResult); - return new ResponseEntity<>(result, HttpStatus.OK); + if (addResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } } - @ApiOperation("删除角色") - @ApiImplicitParams({ - @ApiImplicitParam(name = "id", required = true, value = "用户Id", dataTypeClass = Integer.class), - }) @DeleteMapping("/delete") - public ResponseEntity> delete(@RequestParam Integer id){ + @Operation(summary = "删除角色") + @Parameter(name = "id", description = "用户Id", required = true) + public void delete(@RequestParam Integer id){ // 获取当前登录用户id int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); - WVPResult result = new WVPResult<>(); if (currenRoleId != 1) { // 只用角色id为0才可以删除和添加用户 - result.setCode(-1); - result.setMsg("用户无权限"); - return new ResponseEntity<>(result, HttpStatus.FORBIDDEN); + throw new ControllerException(ErrorCode.ERROR403); } int deleteResult = roleService.delete(id); - result.setCode(deleteResult>0? 0 : -1); - result.setMsg(deleteResult>0? "success" : "fail"); - return new ResponseEntity<>(result, HttpStatus.OK); + if (deleteResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } } - @ApiOperation("查询角色") - @ApiImplicitParams({}) @GetMapping("/all") - public ResponseEntity>> all(){ + @Operation(summary = "查询角色") + public List all(){ // 获取当前登录用户id List allRoles = roleService.getAll(); - WVPResult> result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - result.setData(allRoles); - return new ResponseEntity<>(result, HttpStatus.OK); + return roleService.getAll(); } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java index 17fe2fb6..157cb751 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/user/UserController.java @@ -1,29 +1,29 @@ package com.genersoft.iot.vmp.vmanager.user; +import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.SecurityUtils; import com.genersoft.iot.vmp.conf.security.dto.LoginUser; import com.genersoft.iot.vmp.service.IRoleService; import com.genersoft.iot.vmp.service.IUserService; import com.genersoft.iot.vmp.storager.dao.dto.Role; import com.genersoft.iot.vmp.storager.dao.dto.User; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiImplicitParam; -import io.swagger.annotations.ApiImplicitParams; -import io.swagger.annotations.ApiOperation; +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.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.util.DigestUtils; -import org.springframework.util.StringUtils; +import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; import javax.security.sasl.AuthenticationException; -import java.text.SimpleDateFormat; import java.util.List; -@Api(tags = "用户管理") +@Tag(name = "用户管理") @CrossOrigin @RestController @RequestMapping("/api/user") @@ -38,145 +38,162 @@ public class UserController { @Autowired private IRoleService roleService; - private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - @ApiOperation("登录") - @ApiImplicitParams({ - @ApiImplicitParam(name = "username", required = true, value = "用户名", dataTypeClass = String.class), - @ApiImplicitParam(name = "password", required = true, value = "密码(32位md5加密)", dataTypeClass = String.class), - }) @GetMapping("/login") - public WVPResult login(@RequestParam String username, @RequestParam String password){ + @PostMapping("/login") + @Operation(summary = "登录") + @Parameter(name = "username", description = "用户名", required = true) + @Parameter(name = "password", description = "密码(32位md5加密)", required = true) + public LoginUser login(@RequestParam String username, @RequestParam String password){ LoginUser user = null; - WVPResult result = new WVPResult<>(); try { user = SecurityUtils.login(username, password, authenticationManager); } catch (AuthenticationException e) { - e.printStackTrace(); - result.setCode(-1); - result.setMsg("fail"); + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); } - if (user != null) { - result.setCode(0); - result.setMsg("success"); - result.setData(user); - }else { - result.setCode(-1); - result.setMsg("fail"); + if (user == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "用户名或密码错误"); } - return result; + return user; } - @ApiOperation("修改密码") - @ApiImplicitParams({ - @ApiImplicitParam(name = "username", required = true, value = "用户名", dataTypeClass = String.class), - @ApiImplicitParam(name = "oldpassword", required = true, value = "旧密码(已md5加密的密码)", dataTypeClass = String.class), - @ApiImplicitParam(name = "password", required = true, value = "新密码(未md5加密的密码)", dataTypeClass = String.class), - }) @PostMapping("/changePassword") - public String changePassword(@RequestParam String oldPassword, @RequestParam String password){ + @Operation(summary = "修改密码") + @Parameter(name = "username", description = "用户名", required = true) + @Parameter(name = "oldpassword", description = "旧密码(已md5加密的密码)", required = true) + @Parameter(name = "password", description = "新密码(未md5加密的密码)", required = true) + public void changePassword(@RequestParam String oldPassword, @RequestParam String password){ // 获取当前登录用户id LoginUser userInfo = SecurityUtils.getUserInfo(); if (userInfo== null) { - return "fail"; + throw new ControllerException(ErrorCode.ERROR100); } String username = userInfo.getUsername(); LoginUser user = null; try { user = SecurityUtils.login(username, oldPassword, authenticationManager); - if (user != null) { - int userId = SecurityUtils.getUserId(); - boolean result = userService.changePassword(userId, DigestUtils.md5DigestAsHex(password.getBytes())); - if (result) { - return "success"; - } + if (user == null) { + throw new ControllerException(ErrorCode.ERROR100); + } + int userId = SecurityUtils.getUserId(); + boolean result = userService.changePassword(userId, DigestUtils.md5DigestAsHex(password.getBytes())); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); } } catch (AuthenticationException e) { - e.printStackTrace(); + throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); } - return "fail"; } - @ApiOperation("添加用户") - @ApiImplicitParams({ - @ApiImplicitParam(name = "username", required = true, value = "用户名", dataTypeClass = String.class), - @ApiImplicitParam(name = "password", required = true, value = "密码(未md5加密的密码)", dataTypeClass = String.class), - @ApiImplicitParam(name = "roleId", required = true, value = "角色ID", dataTypeClass = String.class), - }) @PostMapping("/add") - public ResponseEntity> add(@RequestParam String username, + @Operation(summary = "添加用户") + @Parameter(name = "username", description = "用户名", required = true) + @Parameter(name = "password", description = "密码(未md5加密的密码)", required = true) + @Parameter(name = "roleId", description = "角色ID", required = true) + public void add(@RequestParam String username, @RequestParam String password, @RequestParam Integer roleId){ - WVPResult result = new WVPResult<>(); - if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password) || roleId == null) { - result.setCode(-1); - result.setMsg("参数不可为空"); - return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); + if (ObjectUtils.isEmpty(username) || ObjectUtils.isEmpty(password) || roleId == null) { + throw new ControllerException(ErrorCode.ERROR400.getCode(), "参数不可为空"); } // 获取当前登录用户id int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); if (currenRoleId != 1) { // 只用角色id为1才可以删除和添加用户 - result.setCode(-1); - result.setMsg("用户无权限"); - return new ResponseEntity<>(result, HttpStatus.FORBIDDEN); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "用户无权限"); } User user = new User(); user.setUsername(username); user.setPassword(DigestUtils.md5DigestAsHex(password.getBytes())); - + //新增用户的pushKey的生成规则为md5(时间戳+用户名) + user.setPushKey(DigestUtils.md5DigestAsHex((System.currentTimeMillis()+password).getBytes())); Role role = roleService.getRoleById(roleId); if (role == null) { - result.setCode(-1); - result.setMsg("roleId is not found"); - // 角色不存在 - return new ResponseEntity<>(result, HttpStatus.OK); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "角色不存在"); } user.setRole(role); - user.setCreateTime(format.format(System.currentTimeMillis())); - user.setUpdateTime(format.format(System.currentTimeMillis())); + user.setCreateTime(DateUtil.getNow()); + user.setUpdateTime(DateUtil.getNow()); int addResult = userService.addUser(user); - - result.setCode(addResult > 0 ? 0 : -1); - result.setMsg(addResult > 0 ? "success" : "fail"); - result.setData(addResult); - return new ResponseEntity<>(result, HttpStatus.OK); + if (addResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } } - @ApiOperation("删除用户") - @ApiImplicitParams({ - @ApiImplicitParam(name = "id", required = true, value = "用户Id", dataTypeClass = Integer.class), - }) @DeleteMapping("/delete") - public ResponseEntity> delete(@RequestParam Integer id){ + @Operation(summary = "删除用户") + @Parameter(name = "id", description = "用户Id", required = true) + public void delete(@RequestParam Integer id){ + // 获取当前登录用户id + int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); + if (currenRoleId != 1) { + // 只用角色id为0才可以删除和添加用户 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "用户无权限"); + } + int deleteResult = userService.deleteUser(id); + if (deleteResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); + } + } + + @GetMapping("/all") + @Operation(summary = "查询用户") + public List all(){ + // 获取当前登录用户id + return userService.getAllUsers(); + } + + /** + * 分页查询用户 + * + * @param page 当前页 + * @param count 每页查询数量 + * @return 分页用户列表 + */ + @GetMapping("/users") + @Operation(summary = "分页查询用户") + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + public PageInfo users(int page, int count) { + return userService.getUsers(page, count); + } + + @RequestMapping("/changePushKey") + @Operation(summary = "修改pushkey") + @Parameter(name = "userId", description = "用户Id", required = true) + @Parameter(name = "pushKey", description = "新的pushKey", required = true) + public void changePushKey(@RequestParam Integer userId,@RequestParam String pushKey) { // 获取当前登录用户id int currenRoleId = SecurityUtils.getUserInfo().getRole().getId(); WVPResult result = new WVPResult<>(); if (currenRoleId != 1) { // 只用角色id为0才可以删除和添加用户 - result.setCode(-1); - result.setMsg("用户无权限"); - return new ResponseEntity<>(result, HttpStatus.FORBIDDEN); + throw new ControllerException(ErrorCode.ERROR400.getCode(), "用户无权限"); + } + int resetPushKeyResult = userService.changePushKey(userId,pushKey); + if (resetPushKeyResult <= 0) { + throw new ControllerException(ErrorCode.ERROR100); } - int deleteResult = userService.deleteUser(id); - - result.setCode(deleteResult>0? 0 : -1); - result.setMsg(deleteResult>0? "success" : "fail"); - return new ResponseEntity<>(result, HttpStatus.OK); } - @ApiOperation("查询用户") - @ApiImplicitParams({}) - @GetMapping("/all") - public ResponseEntity>> all(){ + @PostMapping("/changePasswordForAdmin") + @Operation(summary = "管理员修改普通用户密码") + @Parameter(name = "adminId", description = "管理员id", required = true) + @Parameter(name = "userId", description = "用户id", required = true) + @Parameter(name = "password", description = "新密码(未md5加密的密码)", required = true) + public void changePasswordForAdmin(@RequestParam int userId, @RequestParam String password) { // 获取当前登录用户id - List allUsers = userService.getAllUsers(); - WVPResult> result = new WVPResult<>(); - result.setCode(0); - result.setMsg("success"); - result.setData(allUsers); - return new ResponseEntity<>(result, HttpStatus.OK); + LoginUser userInfo = SecurityUtils.getUserInfo(); + if (userInfo == null) { + throw new ControllerException(ErrorCode.ERROR100); + } + Role role = userInfo.getRole(); + if (role != null && role.getId() == 1) { + boolean result = userService.changePassword(userId, DigestUtils.md5DigestAsHex(password.getBytes())); + if (!result) { + throw new ControllerException(ErrorCode.ERROR100); + } + } } } 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 eac742c8..be2bbc19 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 @@ -1,14 +1,20 @@ package com.genersoft.iot.vmp.web.gb28181; -import com.alibaba.fastjson.JSONObject; +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.transmit.cmd.impl.SIPCommander; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + /** * API兼容:设备控制 */ @@ -23,7 +29,7 @@ public class ApiControlController { private SIPCommander cmder; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; /** * 设备控制 - 云台控制 @@ -35,7 +41,7 @@ public class ApiControlController { * @return */ @RequestMapping(value = "/ptz") - private JSONObject list(String serial,String command, + private void list(String serial,String command, @RequestParam(required = false)Integer channel, @RequestParam(required = false)String code, @RequestParam(required = false)Integer speed){ @@ -48,9 +54,7 @@ public class ApiControlController { if (speed == null) {speed = 0;} Device device = storager.queryVideoDevice(serial); if (device == null) { - JSONObject result = new JSONObject(); - result.put("error","device[ " + serial + " ]未找到"); - return result; + throw new ControllerException(ErrorCode.ERROR100.getCode(), "device[ " + serial + " ]未找到"); } int cmdCode = 0; switch (command){ @@ -91,7 +95,11 @@ public class ApiControlController { break; } // 默认值 50 - cmder.frontEndCmd(device, code, cmdCode, speed, speed, speed); - return null; + try { + cmder.frontEndCmd(device, code, cmdCode, speed, speed, speed); + } catch (SipException | InvalidArgumentException | ParseException e) { + logger.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 faf873e3..e6286d60 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 @@ -1,6 +1,6 @@ package com.genersoft.iot.vmp.web.gb28181; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.SipConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 2a021a2f..e5f42272 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 @@ -1,16 +1,19 @@ package com.genersoft.iot.vmp.web.gb28181; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; +import com.genersoft.iot.vmp.service.IDeviceService; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import com.github.pagehelper.PageInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; +import java.util.Arrays; import java.util.List; /** @@ -25,7 +28,9 @@ public class ApiDeviceController { private final static Logger logger = LoggerFactory.getLogger(ApiDeviceController.class); @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; + @Autowired + private IDeviceService deviceService; // @Autowired // private SIPCommander cmder; @@ -37,7 +42,7 @@ public class ApiDeviceController { // private DeviceOffLineDetector offLineDetector; /** - * 分页获取设备列表 TODO 现在直接返回,尚未实现分页 + * 分页获取设备列表 现在直接返回,尚未实现分页 * @param start * @param limit * @param q @@ -56,10 +61,10 @@ public class ApiDeviceController { JSONObject result = new JSONObject(); List devices; if (start == null || limit ==null) { - devices = storager.queryVideoDeviceList(); + devices = storager.queryVideoDeviceList(online); result.put("DeviceCount", devices.size()); }else { - PageInfo deviceList = storager.queryVideoDeviceList(start/limit, limit); + PageInfo deviceList = storager.queryVideoDeviceList(start/limit, limit,online); result.put("DeviceCount", deviceList.getTotal()); devices = deviceList.getList(); } @@ -91,6 +96,7 @@ public class ApiDeviceController { @RequestMapping(value = "/channellist") public JSONObject channellist( String serial, + @RequestParam(required = false)String code, @RequestParam(required = false)String channel_type, @RequestParam(required = false)String dir_serial , @RequestParam(required = false)Integer start, @@ -110,13 +116,19 @@ public class ApiDeviceController { return result; } List deviceChannels; + List channelIds = null; + if (!StringUtils.isEmpty(code)) { + String[] split = code.trim().split(","); + channelIds = Arrays.asList(split); + } + List allDeviceChannelList = storager.queryChannelsByDeviceId(serial,online,channelIds); if (start == null || limit ==null) { - deviceChannels = storager.queryChannelsByDeviceId(serial); + deviceChannels = allDeviceChannelList; result.put("ChannelCount", deviceChannels.size()); }else { - PageInfo pageResult = storager.queryChannelsByDeviceId(serial, null, null, null,start/limit, limit); - result.put("ChannelCount", pageResult.getTotal()); - deviceChannels = pageResult.getList(); + deviceChannels = storager.queryChannelsByDeviceIdWithStartAndLimit(serial, null, null, online,start, limit,channelIds); + int total = allDeviceChannelList.size(); + result.put("ChannelCount", total); } JSONArray channleJSONList = new JSONArray(); @@ -126,11 +138,11 @@ public class ApiDeviceController { deviceJOSNChannel.put("DeviceID", device.getDeviceId()); deviceJOSNChannel.put("DeviceName", device.getName()); deviceJOSNChannel.put("DeviceOnline", device.getOnline() == 1); - deviceJOSNChannel.put("Channel", 0); // TODO 自定义序号 + deviceJOSNChannel.put("Channel", 0); // 自定义序号 deviceJOSNChannel.put("Name", deviceChannel.getName()); deviceJOSNChannel.put("Custom", false); deviceJOSNChannel.put("CustomName", ""); - deviceJOSNChannel.put("SubCount", deviceChannel.getSubCount()); // TODO ? 子节点数, SubCount > 0 表示该通道为子目录 + deviceJOSNChannel.put("SubCount", deviceChannel.getSubCount()); // 子节点数, SubCount > 0 表示该通道为子目录 deviceJOSNChannel.put("SnapURL", ""); deviceJOSNChannel.put("Manufacturer ", deviceChannel.getManufacture()); deviceJOSNChannel.put("Model", deviceChannel.getModel()); @@ -144,7 +156,7 @@ public class ApiDeviceController { // 1-IETF RFC3261, // 2-基于口令的双向认证, // 3-基于数字证书的双向认证 - deviceJOSNChannel.put("Status", deviceChannel.getStatus()); + deviceJOSNChannel.put("Status", deviceChannel.getStatus() == 1 ? "ON":"OFF"); deviceJOSNChannel.put("Longitude", deviceChannel.getLongitude()); deviceJOSNChannel.put("Latitude", deviceChannel.getLatitude()); deviceJOSNChannel.put("PTZType ", deviceChannel.getPTZType()); // 云台类型, 0 - 未知, 1 - 球机, 2 - 半球, 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 70f98114..5381a3ae 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 @@ -1,23 +1,27 @@ package com.genersoft.iot.vmp.web.gb28181; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.StreamInfo; -import com.genersoft.iot.vmp.conf.UserSetup; +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.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem; +import com.genersoft.iot.vmp.service.IDeviceService; import com.genersoft.iot.vmp.service.IPlayService; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; -import com.genersoft.iot.vmp.storager.IVideoManagerStorager; -import com.genersoft.iot.vmp.vmanager.gb28181.play.bean.PlayResult; +import com.genersoft.iot.vmp.storager.IVideoManagerStorage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.async.DeferredResult; +import javax.sip.InvalidArgumentException; +import javax.sip.SipException; +import java.text.ParseException; + /** * API兼容:实时直播 */ @@ -33,14 +37,17 @@ public class ApiStreamController { private SIPCommander cmder; @Autowired - private IVideoManagerStorager storager; + private IVideoManagerStorage storager; @Autowired - private UserSetup userSetup; + private UserSetting userSetting; @Autowired private IRedisCatchStorage redisCatchStorage; + @Autowired + private IDeviceService deviceService; + @Autowired private IPlayService playService; @@ -49,12 +56,12 @@ public class ApiStreamController { * @param serial 设备编号 * @param channel 通道序号 默认值: 1 * @param code 通道编号,通过 /api/v1/device/channellist 获取的 ChannelList.ID, 该参数和 channel 二选一传递即可 - * @param cdn TODO 转推 CDN 地址, 形如: [rtmp|rtsp]://xxx, encodeURIComponent - * @param audio TODO 是否开启音频, 默认 开启 + * @param cdn 转推 CDN 地址, 形如: [rtmp|rtsp]://xxx, encodeURIComponent + * @param audio 是否开启音频, 默认 开启 * @param transport 流传输模式, 默认 UDP - * @param checkchannelstatus TODO 是否检查通道状态, 默认 false, 表示 拉流前不检查通道状态是否在线 - * @param transportmode TODO 当 transport=TCP 时有效, 指示流传输主被动模式, 默认被动 - * @param timeout TODO 拉流超时(秒), + * @param checkchannelstatus 是否检查通道状态, 默认 false, 表示 拉流前不检查通道状态是否在线 + * @param transportmode 当 transport=TCP 时有效, 指示流传输主被动模式, 默认被动 + * @param timeout 拉流超时(秒), * @return */ @RequestMapping(value = "/start") @@ -69,16 +76,18 @@ public class ApiStreamController { @RequestParam(required = false)String timeout ){ - DeferredResult resultDeferredResult = new DeferredResult<>(userSetup.getPlayTimeout() + 10); + DeferredResult resultDeferredResult = new DeferredResult<>(userSetting.getPlayTimeout().longValue() + 10); Device device = storager.queryVideoDevice(serial); if (device == null ) { JSONObject result = new JSONObject(); result.put("error","device[ " + serial + " ]未找到"); resultDeferredResult.setResult(result); + return resultDeferredResult; }else if (device.getOnline() == 0) { JSONObject result = new JSONObject(); result.put("error","device[ " + code + " ]offline"); resultDeferredResult.setResult(result); + return resultDeferredResult; } resultDeferredResult.onTimeout(()->{ logger.info("播放等待超时"); @@ -94,25 +103,28 @@ public class ApiStreamController { JSONObject result = new JSONObject(); result.put("error","channel[ " + code + " ]未找到"); resultDeferredResult.setResult(result); + return resultDeferredResult; }else if (deviceChannel.getStatus() == 0) { JSONObject result = new JSONObject(); result.put("error","channel[ " + code + " ]offline"); resultDeferredResult.setResult(result); + return resultDeferredResult; } MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device); - PlayResult play = playService.play(newMediaServerItem, serial, code, (mediaServerItem, response)->{ + playService.play(newMediaServerItem, serial, code, (mediaServerItem, response)->{ StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(serial, code); JSONObject result = new JSONObject(); - result.put("StreamID", streamInfo.getStreamId()); + result.put("StreamID", streamInfo.getStream()); result.put("DeviceID", device.getDeviceId()); result.put("ChannelID", code); result.put("ChannelName", deviceChannel.getName()); result.put("ChannelCustomName", ""); - result.put("FLV", streamInfo.getFlv()); - result.put("WS_FLV", streamInfo.getWs_flv()); - result.put("RTMP", streamInfo.getRtmp()); - result.put("HLS", streamInfo.getHls()); - result.put("RTSP", streamInfo.getRtsp()); + result.put("FLV", streamInfo.getFlv().getUrl()); + result.put("WS_FLV", streamInfo.getWs_flv().getUrl()); + result.put("RTMP", streamInfo.getRtmp().getUrl()); + result.put("HLS", streamInfo.getHls().getUrl()); + result.put("RTSP", streamInfo.getRtsp().getUrl()); + result.put("WEBRTC", streamInfo.getRtc().getUrl()); result.put("CDN", ""); result.put("SnapURL", ""); result.put("Transport", device.getTransport()); @@ -134,23 +146,11 @@ public class ApiStreamController { result.put("RelaySize", ""); result.put("ChannelPTZType", "0"); resultDeferredResult.setResult(result); -// Class aClass = responseEntity.getClass().getSuperclass(); -// Field body = null; -// try { -// // 使用反射动态修改返回的body -// body = aClass.getDeclaredField("body"); -// body.setAccessible(true); -// body.set(responseEntity, result); -// } catch (NoSuchFieldException e) { -// e.printStackTrace(); -// } catch (IllegalAccessException e) { -// e.printStackTrace(); -// } }, (eventResult) -> { JSONObject result = new JSONObject(); result.put("error", "channel[ " + code + " ] " + eventResult.msg); resultDeferredResult.setResult(result); - }); + }, null); return resultDeferredResult; } @@ -177,7 +177,19 @@ public class ApiStreamController { result.put("error","未找到流信息"); return result; } - cmder.streamByeCmd(serial, code); + Device device = deviceService.getDevice(serial); + if (device == null) { + JSONObject result = new JSONObject(); + result.put("error","未找到设备"); + return result; + } + try { + cmder.streamByeCmd(device, code, streamInfo.getStream(), null); + } catch (InvalidArgumentException | ParseException | SipException | SsrcTransactionNotFoundException e) { + JSONObject result = new JSONObject(); + result.put("error","发送BYE失败:" + e.getMessage()); + return result; + } redisCatchStorage.stopPlay(streamInfo); storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); return null; diff --git a/src/main/resources/all-application.yml b/src/main/resources/all-application.yml index 20587424..e3c23604 100644 --- a/src/main/resources/all-application.yml +++ b/src/main/resources/all-application.yml @@ -1,10 +1,15 @@ -# 此配置文件只是用作展示所有配置项, 不可不直接使用 +# 此配置文件只是用作展示所有配置项, 不可直接使用 spring: + # [可选]上传文件大小限制 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB # REDIS数据库配置 redis: # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 @@ -23,26 +28,46 @@ spring: poolMaxIdle: 500 # [可选] 最大的等待时间(秒) poolMaxWait: 5 - # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置 + # [必选] jdbc数据库配置 datasource: - # 使用mysql 打开23-28行注释, 删除29-36行 - # name: wvp - # url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true - # username: - # password: - # type: com.alibaba.druid.pool.DruidDataSource - # driver-class-name: com.mysql.cj.jdbc.Driver - name: eiot - url: jdbc:sqlite::resource:wvp.sqlite - username: - password: type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: org.sqlite.JDBC - journal_mode: WAL - synchronous: NORMAL - transaction_mode: IMMEDIATE - max-active: 1 - min-idle: 1 + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true + username: root + password: root123 + druid: + initialSize: 10 # 连接池初始化连接数 + maxActive: 200 # 连接池最大连接数 + minIdle: 5 # 连接池最小空闲连接数 + maxWait: 60000 # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 + keepAlive: true # 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。 + validationQuery: select 1 # 检测连接是否有效sql,要求是查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + testWhileIdle: true # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 + testOnBorrow: false # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + testOnReturn: false # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + poolPreparedStatements: false # 是否開啟PSCache,並且指定每個連線上PSCache的大小 + timeBetweenEvictionRunsMillis: 60000 # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒 + minEvictableIdleTimeMillis: 300000 # 配置一個連線在池中最小生存的時間,單位是毫秒 + filters: stat,slf4j # 配置监控统计拦截的filters,监控统计用的filter:sta, 日志用的filter:log4j + useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据 + # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000 + #stat-view-servlet.url-pattern: /admin/druid/* + +# druid管理监控页面的一些配置 +rj-druid-manage: + allow: # 访问druid监控页面的IP白名单 + deny: 192.168.1.100 # 访问druid监控页面IP黑名单 + loginUsername: rjAdmin # 访问druid监控页面账号 + loginPassword: rj@2022 # 访问druid监控页面密码 + +#mybatis: +# configuration: +# # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl +# # 返回类型为Map,显示null对应的字段 +# call-setters-on-nulls: true +## [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 # [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 server: @@ -54,9 +79,14 @@ server: # [可选] 证书文件路径,放置在resource/目录下即可,修改xxx为文件名 key-store: classpath:xxx.jks # [可选] 证书密码 - key-password: password + key-store-password: password # [可选] 证书类型, 默认为jks,根据实际修改 key-store-type: JKS + # 配置证书可以使用如下两项,如上面二选一即可 + # PEM 编码证书 + certificate: xx.pem + # 私钥文件 + certificate-private-key: xx.key # 作为28181服务器的配置 sip: @@ -75,8 +105,6 @@ sip: id: 44010200492000000001 # [可选] 默认设备认证密码,后续扩展使用设备单独密码, 移除密码将不进行校验 password: admin123 - # [可选] 心跳超时时间, 建议设置为心跳周期的三倍 - keepalive-timeout: 255 # [可选] 国标级联注册失败,再次发起注册的时间间隔。 默认60秒 register-time-interval: 60 # [可选] 云台控制速度 @@ -88,7 +116,7 @@ sip: #zlm 默认服务器配置 media: - # [可选] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId + # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId id: # [必须修改] zlm服务器的内网IP ip: 192.168.0.100 @@ -116,30 +144,19 @@ media: auto-config: true # [可选] zlm服务器的hook.admin_params=secret secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc - # [可选] zlm服务器的general.streamNoneReaderDelayMS - stream-none-reader-delay-ms: 18000 # 无人观看多久自动关闭流, -1表示永不自动关闭,即 关闭按需拉流 # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 rtp: # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 enable: true - # [可选] 在此范围内选择端口用于媒体流传输, + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 port-range: 30000,30500 # 端口范围 - # [可选] 国标级联在此范围内选择端口发送媒体流, - send-port-range: 30000,30500 # 端口范围 # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 record-assist-port: 0 # [可选] 日志配置, 一般不需要改 logging: - file: - name: logs/wvp.log - max-history: 30 - max-size: 10MB - total-size-cap: 300MB - level: - com.genersoft.iot: debug - com.genersoft.iot.vmp.storager.dao: info - com.genersoft.iot.vmp.gb28181: debug + config: classpath:logback-spring-local.xml + # [根据业务需求配置] user-settings: # [可选] 服务ID,不写则为000000 @@ -150,10 +167,10 @@ user-settings: senior-sdp: false # 保存移动位置历史轨迹:true:保留历史数据,false:仅保留最后的位置(默认) save-position-history: false - # 点播等待超时时间,单位:毫秒 - play-timeout: 3000 - # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 - wait-track: false + # 点播/录像回放 等待超时时间,单位:毫秒 + play-timeout: 18000 + # 上级点播等待超时时间,单位:毫秒 + platform-play-timeout: 60000 # 是否开启接口鉴权 interface-authentication: true # 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 @@ -161,17 +178,31 @@ user-settings: - /api/v1/** # 推流直播是否录制 record-push-live: true + # 国标是否录制 + record-sip: true # 是否将日志存储进数据库 logInDatebase: true - # 第三方匹配,用于从stream钟获取有效信息 - thirdPartyGBIdReg: [\s\S]* + # 使用推流状态作为推流通道状态 + use-pushing-as-status: true + # 使用来源请求ip作为streamIp,当且仅当你只有zlm节点它与wvp在一起的情况下开启 + use-source-ip-as-stream-ip: true + # 国标点播 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放 + stream-on-demand: true + # 推流鉴权, 默认开启 + push-authority: true + # 国标级联发流严格模式,严格模式会使用与sdp信息中一致的端口发流,端口共享media.rtp.port-range,这会损失一些性能, + # 非严格模式使用随机端口发流,性能更好, 默认关闭 + gb-send-stream-strict: false + # 设备上线时是否自动同步通道 + sync-channel-on-device-online: false + # 是否使用设备来源Ip作为回复IP, 不设置则为 false + sip-use-source-ip-as-remote-address: false + # 是否开启sip日志 + sip-log: true -# 在线文档: swagger-ui(生产环境建议关闭) -swagger-ui: - enabled: true - -# 版本信息, 不需修改 -version: - version: "@project.version@" - description: "@project.description@" - artifact-id: "@project.artifactId@" \ No newline at end of file +# 关闭在线文档(生产环境建议关闭) +springdoc: + api-docs: + enabled: false + swagger-ui: + enabled: false diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 066702a0..7d1e116d 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,4 +1,9 @@ spring: + # [可选]上传文件大小限制 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB # REDIS数据库配置 redis: # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 @@ -8,38 +13,43 @@ spring: # [可选] 数据库 DB database: 6 # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 - password: + password: face2020 # [可选] 超时时间 timeout: 10000 - # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置 + # mysql数据源 datasource: - # 使用mysql 打开23-28行注释, 删除29-36行 - # name: wvp - # url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true - # username: - # password: - # type: com.alibaba.druid.pool.DruidDataSource - # driver-class-name: com.mysql.cj.jdbc.Driver - name: eiot - url: jdbc:sqlite::resource:wvp.sqlite - username: - password: type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: org.sqlite.JDBC - journal_mode: WAL - synchronous: NORMAL - transaction_mode: IMMEDIATE - max-active: 1 - min-idle: 1 + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true + username: root + password: 123456 + druid: + initialSize: 10 # 连接池初始化连接数 + maxActive: 200 # 连接池最大连接数 + minIdle: 5 # 连接池最小空闲连接数 + maxWait: 60000 # 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 + keepAlive: true # 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。 + validationQuery: select 1 # 检测连接是否有效sql,要求是查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + testWhileIdle: true # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 + testOnBorrow: false # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + testOnReturn: false # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 + poolPreparedStatements: false # 是否開啟PSCache,並且指定每個連線上PSCache的大小 + timeBetweenEvictionRunsMillis: 60000 # 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒 + minEvictableIdleTimeMillis: 300000 # 配置一個連線在池中最小生存的時間,單位是毫秒 + filters: stat,slf4j # 配置监控统计拦截的filters,监控统计用的filter:sta, 日志用的filter:log4j + useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据 + # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000 + #stat-view-servlet.url-pattern: /admin/druid/* -# [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 +#[可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 server: port: 18080 # 作为28181服务器的配置 sip: # [必须修改] 本机的IP - ip: + ip: 192.168.41.16 # [可选] 28181服务监听的端口 port: 5060 # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) @@ -54,17 +64,18 @@ sip: #zlm 默认服务器配置 media: + id: FQ3TF8yT83wh5Wvz # [必须修改] zlm服务器的内网IP - ip: 127.0.0.1 + ip: 192.168.41.16 # [必须修改] zlm服务器的http.port - http-port: 80 + http-port: 8091 # [可选] zlm服务器的hook.admin_params=secret secret: 035c73f7-bb6b-4889-a715-d9eb2d1925cc # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 rtp: # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 enable: true - # [可选] 在此范围内选择端口用于媒体流传输, + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 port-range: 30000,30500 # 端口范围 # [可选] 国标级联在此范围内选择端口发送媒体流, send-port-range: 30000,30500 # 端口范围 @@ -72,28 +83,4 @@ media: record-assist-port: 18081 # [可选] 日志配置, 一般不需要改 logging: - file: - name: logs/wvp.log - max-history: 30 - max-size: 10MB - total-size-cap: 300MB - level: - com.genersoft.iot: debug - com.genersoft.iot.vmp.storager.dao: info - com.genersoft.iot.vmp.gb28181: info - -# [根据业务需求配置] -user-settings: - # 推流直播是否录制 - record-push-live: true - auto-apply-play: false - -# 在线文档: swagger-ui(生产环境建议关闭) -swagger-ui: - enabled: true - -# 版本信息, 不需修改 -version: - version: "@project.version@" - description: "@project.description@" - artifact-id: "@project.artifactId@" + config: classpath:logback-spring-local.xml diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 7eefe888..a722435c 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -1,4 +1,9 @@ spring: + # 上传文件大小限制 + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB # REDIS数据库配置 redis: # [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1 @@ -11,26 +16,15 @@ spring: password: ${REDIS_PWD:root} # [可选] 超时时间 timeout: 10000 - # [可选] jdbc数据库配置, 项目使用sqlite作为数据库,一般不需要配置 + # [必选] jdbc数据库配置 datasource: # 使用mysql 打开23-28行注释, 删除29-36行 - # name: wvp - # url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true - # username: - # password: - # type: com.alibaba.druid.pool.DruidDataSource - # driver-class-name: com.mysql.cj.jdbc.Driver - name: eiot - url: jdbc:sqlite::resource:wvp.sqlite - username: - password: - type: com.alibaba.druid.pool.DruidDataSource - driver-class-name: org.sqlite.JDBC - journal_mode: WAL - synchronous: NORMAL - transaction_mode: IMMEDIATE - max-active: 1 - min-idle: 1 + name: wvp + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&allowMultiQueries=true&useSSL=false&allowMultiQueries=true + username: root + password: root + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver # [可选] WVP监听的HTTP端口, 网页和接口调用都是这个端口 server: @@ -54,6 +48,8 @@ sip: #zlm 默认服务器配置 media: + # [必须修改] zlm服务器唯一id,用于触发hook时区别是哪台服务器,general.mediaServerId + id: # [必须修改] zlm服务器的内网IP ip: ${ZLM_HOST:127.0.0.1} # [必须修改] zlm服务器的http.port @@ -73,29 +69,12 @@ media: sdp-ip: ${sip.ip} stream-ip: ${sip.ip} # [可选] 日志配置, 一般不需要改 +# [可选] 日志配置, 一般不需要改 logging: - file: - name: logs/wvp.log - max-history: 30 - max-size: 10MB - total-size-cap: 300MB - level: - com.genersoft.iot: debug - com.genersoft.iot.vmp.storager.dao: info - com.genersoft.iot.vmp.gb28181: info + config: classpath:logback-spring-local.xml # [根据业务需求配置] user-settings: # 推流直播是否录制 record-push-live: true auto-apply-play: true - -# 在线文档: swagger-ui(生产环境建议关闭) -swagger-ui: - enabled: true - -# 版本信息, 不需修改 -version: - version: "@project.version@" - description: "@project.description@" - artifact-id: "@project.artifactId@" diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3d7808a0..d74c444c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,3 @@ spring: profiles: - active: dev + active: local diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt index 0c7250d4..7a75e1c6 100644 --- a/src/main/resources/banner.txt +++ b/src/main/resources/banner.txt @@ -5,5 +5,3 @@ \ \ \|\__\_\ \ \ / / \ \ \___\|____________|\ \ \___|\ \ \\ \\ \ \\\ \ \ \____________\ \__/ / \ \__\ \ \__\ \ \__\\ _\\ \_______\ \|____________|\|__|/ \|__| \|__| \|__|\|__|\|_______| - -版本:${version.version} \ No newline at end of file diff --git a/src/main/resources/logback-spring-local.xml b/src/main/resources/logback-spring-local.xml new file mode 100644 index 00000000..724d05e4 --- /dev/null +++ b/src/main/resources/logback-spring-local.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n + + + + + DEBUG + + + + + + + + + + ${LOG_HOME}/wvp-%d{yyyy-MM-dd}.%i.log + + 30 + 20MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n + + + + + DEBUG + + + + + + + + + ${LOG_HOME}/error-%d{yyyy-MM-dd}.%i.log + + 30 + 20MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n + + + + WARN + + + + + + + + + + ${LOG_HOME}/druid-%d{yyyy-MM-dd}.%i.log + + 30 + 50MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n + + + + + + + + ${LOG_HOME}/sip-%d{yyyy-MM-dd}.%i.log + + 30 + 50MB + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/wvp.sqlite b/src/main/resources/wvp.sqlite deleted file mode 100644 index e6140f4e..00000000 Binary files a/src/main/resources/wvp.sqlite and /dev/null differ diff --git a/src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java b/src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java deleted file mode 100644 index 3cb9aa56..00000000 --- a/src/test/java/com/genersoft/iot/vmp/service/impl/DeviceAlarmServiceImplTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.genersoft.iot.vmp.service.impl; - -import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; -import com.genersoft.iot.vmp.service.IDeviceAlarmService; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -import javax.annotation.Resource; -import java.text.SimpleDateFormat; -import java.util.Date; - - -@SpringBootTest -@RunWith(SpringRunner.class) -class DeviceAlarmServiceImplTest { - - @Resource - private IDeviceAlarmService deviceAlarmService; - - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - @org.junit.jupiter.api.Test - void getAllAlarm() { -// deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111",null,null,null, null, null); -// System.out.println(deviceAlarmService.getAllAlarm(0, 10000, null, null, null, null, -// null, null).getSize()); -// -// System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", null, null, null, -// null, null).getSize()); -// -// System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", "1", null, null, -// null, null).getSize()); -// -// System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", "2", null, null, -// null, null).getSize()); -// -// System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", "3", null, null, -// null, null).getSize()); -// -// System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", "4", null, null, -// null, null).getSize()); -// -// System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", "5", null, null, -// null, null).getSize()); -// -// System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", null, "1", null, -// null, null).getSize()); - -// System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", null, "1", null, -// null, null).getSize()); - - System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", null, null, null, - "2021-01-01 00:00:00", null).getSize()); - - System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", null, null, null, - null, "2021-04-01 09:00:00").getSize()); - - System.out.println(deviceAlarmService.getAllAlarm(0, 10000, "11111111111111111111", null, null, null, - "2021-02-01 01:00:00", "2021-04-01 04:00:00").getSize()); - } - - - @org.junit.jupiter.api.Test - void add() { - for (int i = 0; i < 1000; i++) { - DeviceAlarm deviceAlarm = new DeviceAlarm(); - deviceAlarm.setDeviceId("11111111111111111111"); - deviceAlarm.setAlarmDescription("test_" + i); - - /** - * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, - * * 7其他报警;可以为直接组合如12为电话报警或 设备报警- - */ - deviceAlarm.setAlarmMethod((int)(Math.random()*7 + 1) + ""); - Date date = randomDate("2021-01-01 00:00:00", "2021-06-01 00:00:00"); - deviceAlarm.setAlarmTime(format.format(date)); - /** - * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情- - */ - deviceAlarm.setAlarmPriority((int)(Math.random()*4 + 1) + ""); - deviceAlarm.setLongitude(116.325); - deviceAlarm.setLatitude(39.562); - deviceAlarmService.add(deviceAlarm); - } - - } - - @org.junit.jupiter.api.Test - void clearAlarmBeforeTime() { - deviceAlarmService.clearAlarmBeforeTime(null,null, null); - } - - - - - private Date randomDate(String beginDate, String endDate) { - try { - - Date start = format.parse(beginDate);//构造开始日期 - Date end = format.parse(endDate);//构造结束日期 - //getTime()表示返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。 - if (start.getTime() >= end.getTime()) { - return null; - } - long date = random(start.getTime(), end.getTime()); - return new Date(date); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - private static long random(long begin, long end) { - long rtn = begin + (long) (Math.random() * (end - begin)); - //如果返回的是开始时间和结束时间,则递归调用本函数查找随机值 - if (rtn == begin || rtn == end) { - return random(begin, end); - } - return rtn; - } -} \ No newline at end of file diff --git a/src/test/java/com/genersoft/iot/vmp/service/impl/RoleServiceImplTest.java b/src/test/java/com/genersoft/iot/vmp/service/impl/RoleServiceImplTest.java deleted file mode 100644 index 13479fe2..00000000 --- a/src/test/java/com/genersoft/iot/vmp/service/impl/RoleServiceImplTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.genersoft.iot.vmp.service.impl; - -import com.genersoft.iot.vmp.service.IRoleService; -import com.genersoft.iot.vmp.service.IUserService; -import com.genersoft.iot.vmp.storager.dao.dto.Role; -import com.genersoft.iot.vmp.storager.dao.dto.User; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -import javax.annotation.Resource; -import java.text.SimpleDateFormat; -import java.util.List; - - -@SpringBootTest -@RunWith(SpringRunner.class) -class RoleServiceImplTest { - - @Resource - private IRoleService roleService; - - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - @org.junit.jupiter.api.Test - void getAllUser() { - List all = roleService.getAll(); - Role roleById = roleService.getRoleById(1); - - } - - - @org.junit.jupiter.api.Test - void add() { - for (int i = 0; i < 10; i++) { - Role role = new Role(); - role.setName("test+" + i); - role.setAuthority("adadadda"); - role.setCreateTime(format.format(System.currentTimeMillis())); - role.setUpdateTime(format.format(System.currentTimeMillis())); - roleService.add(role); - } - } - - @org.junit.jupiter.api.Test - void delete() { - roleService.delete(20); - } - - @org.junit.jupiter.api.Test - void update() { - Role role = new Role(); - role.setId(21); - role.setName("TTTTTT"); - role.setAuthority("adadadda"); - roleService.update(role); - } -} \ No newline at end of file diff --git a/src/test/java/com/genersoft/iot/vmp/service/impl/UserServiceImplTest.java b/src/test/java/com/genersoft/iot/vmp/service/impl/UserServiceImplTest.java deleted file mode 100644 index 41148706..00000000 --- a/src/test/java/com/genersoft/iot/vmp/service/impl/UserServiceImplTest.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.genersoft.iot.vmp.service.impl; - -import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; -import com.genersoft.iot.vmp.service.IDeviceAlarmService; -import com.genersoft.iot.vmp.service.IUserService; -import com.genersoft.iot.vmp.storager.dao.dto.Role; -import com.genersoft.iot.vmp.storager.dao.dto.User; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -import javax.annotation.Resource; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; - - -@SpringBootTest -@RunWith(SpringRunner.class) -class UserServiceImplTest { - - @Resource - private IUserService userService; - - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - @org.junit.jupiter.api.Test - void getAllUser() { - List allUsers = userService.getAllUsers(); - User admin = userService.getUser("admin", "21232f297a57a5a743894a0e4a801fc3"); - User admin1 = userService.getUserByUsername("admin"); - } - - - @org.junit.jupiter.api.Test - void add() { - for (int i = 0; i < 10; i++) { - User user = new User(); - user.setUsername("admin_" + i); - user.setPassword("admin_password_" + i); - - Role role = new Role(); - role.setId(1); - user.setRole(role); - user.setCreateTime(format.format(System.currentTimeMillis())); - user.setUpdateTime(format.format(System.currentTimeMillis())); - userService.addUser(user); - } - } - - @org.junit.jupiter.api.Test - void delete() { - userService.deleteUser(1002); - } - - @org.junit.jupiter.api.Test - void update() { - User user = new User(); - user.setId(11); - user.setUsername("update" ); - user.setPassword("update"); - Role role = new Role(); - role.setId(2); - user.setRole(role); - user.setUpdateTime(format.format(System.currentTimeMillis())); - userService.updateUsers(user); - } - - -} \ No newline at end of file diff --git a/web_src/build/webpack.dev.conf.js b/web_src/build/webpack.dev.conf.js index 070ae221..ad7c5306 100755 --- a/web_src/build/webpack.dev.conf.js +++ b/web_src/build/webpack.dev.conf.js @@ -32,6 +32,7 @@ const devWebpackConfig = merge(baseWebpackConfig, { contentBase: false, // since we use CopyWebpackPlugin. compress: true, host: HOST || config.dev.host, + // host:'127.0.0.1', port: PORT || config.dev.port, open: config.dev.autoOpenBrowser, overlay: config.dev.errorOverlay @@ -64,6 +65,11 @@ const devWebpackConfig = merge(baseWebpackConfig, { to: config.dev.assetsSubDirectory, ignore: ['.*'] } + ]), + new CopyWebpackPlugin([ + { from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'}, + { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'}, + { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: config.build.assetsSubDirectory + '/js/'}, ]) ] }) diff --git a/web_src/build/webpack.prod.conf.js b/web_src/build/webpack.prod.conf.js index 8ad9ec3d..e4324a94 100644 --- a/web_src/build/webpack.prod.conf.js +++ b/web_src/build/webpack.prod.conf.js @@ -115,6 +115,11 @@ const webpackConfig = merge(baseWebpackConfig, { to: config.build.assetsSubDirectory, ignore: ['.*'] } + ]), + new CopyWebpackPlugin([ + { from: 'node_modules/@liveqing/liveplayer/dist/component/crossdomain.xml'}, + { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer.swf'}, + { from: 'node_modules/@liveqing/liveplayer/dist/component/liveplayer-lib.min.js', to: config.build.assetsSubDirectory + '/js/'}, ]) ] }) diff --git a/web_src/config/index.js b/web_src/config/index.js index cec91b87..c5a74528 100644 --- a/web_src/config/index.js +++ b/web_src/config/index.js @@ -12,14 +12,14 @@ module.exports = { assetsPublicPath: '/', proxyTable: { '/debug': { - target: 'http://localhost:18080', + target: 'http://localhost:38080', changeOrigin: true, pathRewrite: { '^/debug': '/' } }, '/static/snap': { - target: 'http://localhost:18080', + target: 'http://localhost:38080', changeOrigin: true, // pathRewrite: { // '^/static/snap': '/static/snap' @@ -29,11 +29,13 @@ module.exports = { }, // Various Dev Server settings - host: 'localhost', // can be overwritten by process.env.HOST + host:"127.0.0.1", + useLocalIp: false, // can be overwritten by process.env.HOST port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined autoOpenBrowser: false, errorOverlay: true, notifyOnErrors: true, + hot: true,//自动保存 poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- diff --git a/web_src/index.html b/web_src/index.html index 697e987f..e338faad 100644 --- a/web_src/index.html +++ b/web_src/index.html @@ -4,15 +4,16 @@ 国标28181 + - + + - +
- diff --git a/web_src/package-lock.json b/web_src/package-lock.json index 2e015944..c6e972a9 100644 --- a/web_src/package-lock.json +++ b/web_src/package-lock.json @@ -1,15 +1,14522 @@ { "name": "gb_web", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "gb_web", + "version": "1.0.0", + "dependencies": { + "@liveqing/liveplayer": "^2.7.0", + "axios": "^0.24.0", + "core-js": "^2.6.5", + "echarts": "^4.9.0", + "element-ui": "^2.15.6", + "fingerprintjs2": "^2.1.2", + "moment": "^2.29.1", + "ol": "^6.14.1", + "postcss-pxtorem": "^5.1.1", + "uuid": "^8.3.2", + "v-charts": "^1.19.0", + "vue": "^2.6.11", + "vue-clipboard2": "^0.3.1", + "vue-clipboards": "^1.3.0", + "vue-contextmenujs": "^1.3.13", + "vue-cookies": "^1.7.4", + "vue-giant-tree": "^0.1.5", + "vue-router": "^3.1.6", + "vue-ztree-2.0": "^1.0.4" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.11", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^2.30.1", + "node-notifier": "^5.1.2", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^13.3.0", + "vue-style-loader": "^3.0.1", + "vue-template-compiler": "^2.5.2", + "webpack": "^3.6.0", + "webpack-bundle-analyzer": "^2.9.0", + "webpack-dev-server": "^2.9.1", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/@liveqing/liveplayer": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/@liveqing/liveplayer/-/liveplayer-2.7.0.tgz", + "integrity": "sha512-SWveQRqhhfJzkcpmHZxL6eLn+xLQuub888/JiBtUDHgt1eVwYYsorDiGcAKciNcyD70PuMfQ3+QrLoLbWE2vWA==" + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-style-spec": { + "version": "13.23.1", + "resolved": "https://registry.npmmirror.com/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.23.1.tgz", + "integrity": "sha512-C6wh8A/5EdsgzhL6y6yl464VCQNIxK0yjrpnvCvchcFe3sNK2RbBw/J9u3m+p8Y6S6MsGuSMt3AkGAXOKMYweQ==", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/unitbezier": "^0.0.0", + "csscolorparser": "~1.0.2", + "json-stringify-pretty-compact": "^2.0.0", + "minimist": "^1.2.5", + "rw": "^1.3.3", + "sort-object": "^0.3.2" + }, + "bin": { + "gl-style-composite": "bin/gl-style-composite", + "gl-style-format": "bin/gl-style-format", + "gl-style-migrate": "bin/gl-style-migrate", + "gl-style-validate": "bin/gl-style-validate" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmmirror.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==" + }, + "node_modules/@petamoriken/float16": { + "version": "3.6.3", + "resolved": "https://registry.npmmirror.com/@petamoriken/float16/-/float16-3.6.3.tgz", + "integrity": "sha512-Yx6Z93kmz3JVPYoPPRFJXnt2/G4kfaxRROcZVVHsE4zOClJXvkOVidv/JfvP6hWn16lykbKYKVzUsId6mqXdGg==" + }, + "node_modules/@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", + "dev": true + }, + "node_modules/@ztree/ztree_v3": { + "version": "3.5.48", + "resolved": "https://registry.npmmirror.com/@ztree/ztree_v3/-/ztree_v3-3.5.48.tgz", + "integrity": "sha512-4dSA1g26T3j/O3I89+r/Palg+a+xwMGRS1etZoggnCGBPoOrwW8VGA3zitJCK/Yd7eEMX+LfKTRJjEGiWpoN3w==", + "dependencies": { + "jquery": ">=1.4.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz", + "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", + "dev": true, + "dependencies": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "5.7.4", + "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-5.7.4.tgz", + "integrity": "sha1-Po2KmUfQWZoXltECJddDL0pKz14=", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/acorn-dynamic-import/download/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "deprecated": "This is probably built in to whatever tool you're using. If you still need it... idk", + "dev": true, + "dependencies": { + "acorn": "^4.0.3" + } + }, + "node_modules/acorn-dynamic-import/node_modules/acorn": { + "version": "4.0.13", + "resolved": "https://registry.npm.taobao.org/acorn/download/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "5.5.2", + "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-5.5.2.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "dependencies": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npm.taobao.org/ajv-keywords/download/ajv-keywords-3.5.2.tgz", + "integrity": "sha1-MfKdpatuANHC0yms97WSlhTVAU0=", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/align-text": { + "version": "0.1.4", + "resolved": "https://registry.npm.taobao.org/align-text/download/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/alphanum-sort/download/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "node_modules/ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npm.taobao.org/ansi-html/download/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-3.2.1.tgz", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-3.1.1.tgz", + "integrity": "sha1-xV7PAhheJGklk5kxDBc84xIzsUI=", + "dev": true, + "optional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/aproba/download/aproba-1.2.0.tgz", + "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/arr-diff/download/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/arr-flatten/download/arr-flatten-1.1.0.tgz", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/arr-union/download/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/array-find-index/download/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/array-includes/download/array-includes-3.1.1.tgz", + "integrity": "sha1-zdZ+aFK9+cEhVGB4ZzIlXtJFk0g=", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/array-union/download/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/array-uniq/download/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npm.taobao.org/array-unique/download/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npm.taobao.org/asn1.js/download/asn1.js-5.4.1.tgz", + "integrity": "sha1-EamAuE67kXgc41sP3C7ilON4Pwc=", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npm.taobao.org/assert/download/assert-1.5.0.tgz", + "integrity": "sha1-VcEJqvbgrv2z3EtxJAxwv1dLGOs=", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npm.taobao.org/util/download/util-0.10.3.tgz?cache=0&sync_timestamp=1596697422093&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Futil%2Fdownload%2Futil-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/assign-symbols/download/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npm.taobao.org/async/download/async-2.6.3.tgz", + "integrity": "sha1-1yYl4jRKNlbjo61Pp0n6gymdgv8=", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/async-each/download/async-each-1.0.3.tgz", + "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=", + "dev": true + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/async-limiter/download/async-limiter-1.0.1.tgz", + "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=", + "dev": true + }, + "node_modules/async-validator": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.8.5.tgz", + "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==", + "dependencies": { + "babel-runtime": "6.x" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/atob/download/atob-2.1.2.tgz", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "7.2.6", + "resolved": "https://registry.npm.taobao.org/autoprefixer/download/autoprefixer-7.2.6.tgz?cache=0&sync_timestamp=1601167517316&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fautoprefixer%2Fdownload%2Fautoprefixer-7.2.6.tgz", + "integrity": "sha1-JWZy+G98c12oScTwfQCKuwVgZ9w=", + "dev": true, + "dependencies": { + "browserslist": "^2.11.3", + "caniuse-lite": "^1.0.30000805", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.17", + "postcss-value-parser": "^3.2.3" + }, + "bin": { + "autoprefixer-info": "bin/autoprefixer-info" + } + }, + "node_modules/axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "dependencies": { + "follow-redirects": "^1.14.4" + } + }, + "node_modules/axios/node_modules/follow-redirects": { + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", + "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-code-frame/download/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "node_modules/babel-code-frame/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-code-frame/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npm.taobao.org/babel-core/download/babel-core-6.26.3.tgz", + "integrity": "sha1-suLwnjQtDwyI4vAuBneUEl51wgc=", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "node_modules/babel-core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npm.taobao.org/babel-generator/download/babel-generator-6.26.1.tgz", + "integrity": "sha1-GERAjTuPDTWkBOp6wYDwh6YBvZA=", + "dev": true, + "dependencies": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "node_modules/babel-generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-helper-bindify-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-bindify-decorators/download/babel-helper-bindify-decorators-6.24.1.tgz", + "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-builder-binary-assignment-operator-visitor/download/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "dependencies": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-call-delegate/download/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "dependencies": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-helper-define-map/download/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "dependencies": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-explode-assignable-expression/download/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-explode-class": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-explode-class/download/babel-helper-explode-class-6.24.1.tgz", + "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", + "dev": true, + "dependencies": { + "babel-helper-bindify-decorators": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-function-name/download/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "dependencies": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-get-function-arity/download/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-hoist-variables/download/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-optimise-call-expression/download/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-helper-regex/download/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-remap-async-to-generator/download/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "dependencies": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helper-replace-supers/download/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "dependencies": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-helper-vue-jsx-merge-props": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/babel-helper-vue-jsx-merge-props/download/babel-helper-vue-jsx-merge-props-2.0.3.tgz", + "integrity": "sha1-Iq69OzOQIyjlEyk6jkmSs4T58bY=" + }, + "node_modules/babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-helpers/download/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-loader": { + "version": "7.1.5", + "resolved": "https://registry.npm.taobao.org/babel-loader/download/babel-loader-7.1.5.tgz", + "integrity": "sha1-4+4M1zlKpVfgE7AtPkkr/QeqbWg=", + "dev": true, + "dependencies": { + "find-cache-dir": "^1.0.0", + "loader-utils": "^1.0.2", + "mkdirp": "^0.5.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "babel-core": "6", + "webpack": "2 || 3 || 4" + } + }, + "node_modules/babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npm.taobao.org/babel-messages/download/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-check-es2015-constants/download/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-syntax-async-functions/download/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "node_modules/babel-plugin-syntax-async-generators": { + "version": "6.13.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-syntax-async-generators/download/babel-plugin-syntax-async-generators-6.13.0.tgz", + "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", + "dev": true + }, + "node_modules/babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-syntax-class-properties/download/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", + "dev": true + }, + "node_modules/babel-plugin-syntax-decorators": { + "version": "6.13.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-syntax-decorators/download/babel-plugin-syntax-decorators-6.13.0.tgz", + "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", + "dev": true + }, + "node_modules/babel-plugin-syntax-dynamic-import": { + "version": "6.18.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-syntax-dynamic-import/download/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", + "dev": true + }, + "node_modules/babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-syntax-exponentiation-operator/download/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "node_modules/babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-syntax-jsx/download/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", + "dev": true + }, + "node_modules/babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-syntax-object-rest-spread/download/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "dev": true + }, + "node_modules/babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-syntax-trailing-function-commas/download/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "node_modules/babel-plugin-transform-async-generator-functions": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-async-generator-functions/download/babel-plugin-transform-async-generator-functions-6.24.1.tgz", + "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", + "dev": true, + "dependencies": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-async-to-generator/download/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "dependencies": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-class-properties/download/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", + "dev": true, + "dependencies": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-decorators/download/babel-plugin-transform-decorators-6.24.1.tgz", + "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", + "dev": true, + "dependencies": { + "babel-helper-explode-class": "^6.24.1", + "babel-plugin-syntax-decorators": "^6.13.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-arrow-functions/download/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-block-scoped-functions/download/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-block-scoping/download/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-classes/download/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "dependencies": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-computed-properties/download/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-destructuring/download/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-duplicate-keys/download/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-for-of/download/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-function-name/download/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "dependencies": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-literals/download/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-modules-amd/download/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "dependencies": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-modules-commonjs/download/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha1-WKeThjqefKhwvcWogRF/+sJ9tvM=", + "dev": true, + "dependencies": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "node_modules/babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-modules-systemjs/download/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "dependencies": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-modules-umd/download/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "dependencies": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-object-super/download/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "dependencies": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-parameters/download/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "dependencies": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-shorthand-properties/download/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz?cache=0&sync_timestamp=1589682670915&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-plugin-transform-es2015-shorthand-properties%2Fdownload%2Fbabel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-spread/download/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-sticky-regex/download/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "dependencies": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-template-literals/download/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-typeof-symbol/download/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-es2015-unicode-regex/download/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "dependencies": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "node_modules/babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-exponentiation-operator/download/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "dependencies": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-object-rest-spread/download/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "dev": true, + "dependencies": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "node_modules/babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-regenerator/download/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "dependencies": { + "regenerator-transform": "^0.10.0" + } + }, + "node_modules/babel-plugin-transform-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-runtime/download/babel-plugin-transform-runtime-6.23.0.tgz", + "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0" + } + }, + "node_modules/babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-strict-mode/download/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "node_modules/babel-plugin-transform-vue-jsx": { + "version": "3.7.0", + "resolved": "https://registry.npm.taobao.org/babel-plugin-transform-vue-jsx/download/babel-plugin-transform-vue-jsx-3.7.0.tgz", + "integrity": "sha1-1ASS5mkqNrWU9+mhko9D6Wl0CWA=", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "peerDependencies": { + "babel-helper-vue-jsx-merge-props": "^2.0.0" + } + }, + "node_modules/babel-preset-env": { + "version": "1.7.0", + "resolved": "https://registry.npm.taobao.org/babel-preset-env/download/babel-preset-env-1.7.0.tgz?cache=0&sync_timestamp=1591204557603&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-preset-env%2Fdownload%2Fbabel-preset-env-1.7.0.tgz", + "integrity": "sha1-3qefpOvriDzTXasH4mDBycBN93o=", + "dev": true, + "dependencies": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^3.2.6", + "invariant": "^2.2.2", + "semver": "^5.3.0" + } + }, + "node_modules/babel-preset-env/node_modules/browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-3.2.8.tgz", + "integrity": "sha1-sABTYdZHHw9ZUnl6dvyYXx+Xj8Y=", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + }, + "bin": { + "browserslist": "cli.js" + } + }, + "node_modules/babel-preset-stage-2": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-preset-stage-2/download/babel-preset-stage-2-6.24.1.tgz", + "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", + "dev": true, + "dependencies": { + "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators": "^6.24.1", + "babel-preset-stage-3": "^6.24.1" + } + }, + "node_modules/babel-preset-stage-3": { + "version": "6.24.1", + "resolved": "https://registry.npm.taobao.org/babel-preset-stage-3/download/babel-preset-stage-3-6.24.1.tgz", + "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", + "dev": true, + "dependencies": { + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-generator-functions": "^6.24.1", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-exponentiation-operator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.22.0" + } + }, + "node_modules/babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-register/download/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "dependencies": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + } + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-runtime/download/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-template/download/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-traverse/download/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "node_modules/babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npm.taobao.org/babel-types/download/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "node_modules/babylon": { + "version": "6.18.0", + "resolved": "https://registry.npm.taobao.org/babylon/download/babylon-6.18.0.tgz", + "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=", + "dev": true, + "bin": { + "babylon": "bin/babylon.js" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npm.taobao.org/base/download/base-0.11.2.tgz", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npm.taobao.org/base64-js/download/base64-js-1.3.1.tgz", + "integrity": "sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=", + "dev": true + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npm.taobao.org/batch/download/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "node_modules/bfj-node4": { + "version": "5.3.1", + "resolved": "https://registry.npm.taobao.org/bfj-node4/download/bfj-node4-5.3.1.tgz", + "integrity": "sha1-4j2LJwV/HQIU/FYRQq2duZjyaDA=", + "deprecated": "Switch to the `bfj` package for fixes and new features!", + "dev": true, + "dependencies": { + "bluebird": "^3.5.1", + "check-types": "^7.3.0", + "tryer": "^1.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npm.taobao.org/big.js/download/big.js-5.2.2.tgz", + "integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-2.1.0.tgz?cache=0&sync_timestamp=1593261283449&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbinary-extensions%2Fdownload%2Fbinary-extensions-2.1.0.tgz", + "integrity": "sha1-MPpAyef+B9vIlWeM0ocCTeokHdk=", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npm.taobao.org/bindings/download/bindings-1.5.0.tgz", + "integrity": "sha1-EDU8npRTNLwFEabZCzj7x8nFBN8=", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npm.taobao.org/bluebird/download/bluebird-3.7.2.tgz", + "integrity": "sha1-nyKcFb4nJFT/qXOs4NvueaGww28=", + "dev": true + }, + "node_modules/bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-5.1.3.tgz", + "integrity": "sha1-vsoAVAj2Quvr6oCwQrTRjSrA7ms=", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npm.taobao.org/body-parser/download/body-parser-1.19.0.tgz", + "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", + "dev": true, + "dependencies": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npm.taobao.org/bonjour/download/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/bonjour/node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/array-flatten/download/array-flatten-2.1.2.tgz", + "integrity": "sha1-JO+AoowaiTYX4hSbDG0NeIKTsJk=", + "dev": true + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/boolbase/download/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz?cache=0&sync_timestamp=1601898201980&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbrace-expansion%2Fdownload%2Fbrace-expansion-1.1.11.tgz", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/braces/download/braces-3.0.2.tgz", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", + "dev": true, + "optional": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/brorand/download/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/browserify-aes/download/browserify-aes-1.2.0.tgz", + "integrity": "sha1-Mmc0ZC9APavDADIJhTu3CtQo70g=", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/browserify-cipher/download/browserify-cipher-1.0.1.tgz", + "integrity": "sha1-jWR0wbhwv9q807z8wZNKEOlPFfA=", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/browserify-des/download/browserify-des-1.0.2.tgz", + "integrity": "sha1-OvTx9Zg5QDVy8cZiBDdfen9wPpw=", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npm.taobao.org/browserify-rsa/download/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-rsa/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npm.taobao.org/browserify-sign/download/browserify-sign-4.2.1.tgz?cache=0&sync_timestamp=1596557809886&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbrowserify-sign%2Fdownload%2Fbrowserify-sign-4.2.1.tgz", + "integrity": "sha1-6vSt1G3VS+O7OzbAzxWrvrp5VsM=", + "dev": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-sign/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.1.tgz", + "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=", + "dev": true + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npm.taobao.org/browserify-zlib/download/browserify-zlib-0.2.0.tgz", + "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "2.11.3", + "resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-2.11.3.tgz", + "integrity": "sha1-/jYWeu0bvN5IJ+v+cTR6LMcLmbI=", + "deprecated": "Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30000792", + "electron-to-chromium": "^1.3.30" + }, + "bin": { + "browserslist": "cli.js" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npm.taobao.org/buffer/download/buffer-4.9.2.tgz", + "integrity": "sha1-Iw6tNEACmIZEhBqwJEr4xEu+Pvg=", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz", + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", + "dev": true + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/buffer-indexof/download/buffer-indexof-1.1.1.tgz", + "integrity": "sha1-Uvq8xqYG0aADAoAmSO9o9jnaJow=", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/buffer-xor/download/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/builtin-status-codes/download/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/bytes/download/bytes-3.1.0.tgz", + "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/cache-base/download/cache-base-1.0.1.tgz", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/caller-callsite/download/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/caller-path/download/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/callsites/download/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/camel-case/download/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/camelcase-keys/download/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "dependencies": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npm.taobao.org/caniuse-api/download/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "dev": true, + "dependencies": { + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-api/node_modules/browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "deprecated": "Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.", + "dev": true, + "dependencies": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + }, + "bin": { + "browserslist": "cli.js" + } + }, + "node_modules/caniuse-db": { + "version": "1.0.30001244", + "resolved": "https://registry.nlark.com/caniuse-db/download/caniuse-db-1.0.30001244.tgz?cache=0&sync_timestamp=1626154539434&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcaniuse-db%2Fdownload%2Fcaniuse-db-1.0.30001244.tgz", + "integrity": "sha1-pt/zJHNkjfCwrg+Z2YeXrft89Fk=", + "dev": true + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001230", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz", + "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==", + "dev": true + }, + "node_modules/center-align": { + "version": "0.1.3", + "resolved": "https://registry.npm.taobao.org/center-align/download/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "dependencies": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-2.4.2.tgz", + "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-types": { + "version": "7.4.0", + "resolved": "https://registry.npm.taobao.org/check-types/download/check-types-7.4.0.tgz", + "integrity": "sha1-A3jsG5YW7HH3dJMaPGUW+tjBUvQ=", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.4.2", + "resolved": "https://registry.npm.taobao.org/chokidar/download/chokidar-3.4.2.tgz?cache=0&sync_timestamp=1597763177396&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchokidar%2Fdownload%2Fchokidar-3.4.2.tgz", + "integrity": "sha1-ONyOZY3sOAl0HrPve7Ckf+QkIy0=", + "dev": true, + "optional": true, + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.1.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/chownr/download/chownr-1.1.4.tgz", + "integrity": "sha1-b8nXtC0ypYNZYzdmbn0ICE2izGs=", + "dev": true + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/cipher-base/download/cipher-base-1.0.4.tgz", + "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/clap": { + "version": "1.2.3", + "resolved": "https://registry.npm.taobao.org/clap/download/clap-1.2.3.tgz", + "integrity": "sha1-TzZ0WzIAhJJVf0ZBLWbVDLmbzlE=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clap/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npm.taobao.org/class-utils/download/class-utils-0.3.6.tgz", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npm.taobao.org/clean-css/download/clean-css-4.2.3.tgz", + "integrity": "sha1-UHtd59l7SO5T2ErbAWD/YhY4D3g=", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/cli-cursor/download/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-spinners": { + "version": "1.3.1", + "resolved": "https://registry.npm.taobao.org/cli-spinners/download/cli-spinners-1.3.1.tgz", + "integrity": "sha1-ACwZkJEtDVlYDJO9NsBW3pnkJZo=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/clipboard": { + "version": "2.0.6", + "resolved": "https://registry.npm.taobao.org/clipboard/download/clipboard-2.0.6.tgz", + "integrity": "sha1-UpISlu7A/fd+rRdJQhshyWhkc3Y=", + "dependencies": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "node_modules/cliui": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/cliui/download/cliui-2.1.0.tgz?cache=0&sync_timestamp=1597606145227&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "dependencies": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/clone/download/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npm.taobao.org/co/download/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/coa": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/coa/download/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "dev": true, + "dependencies": { + "q": "^1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/collection-visit/download/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "0.11.4", + "resolved": "https://registry.npm.taobao.org/color/download/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "dev": true, + "dependencies": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-1.9.3.tgz", + "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/color-string": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/color-string/download/color-string-0.3.0.tgz?cache=0&sync_timestamp=1602228058149&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcolor-string%2Fdownload%2Fcolor-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "dev": true, + "dependencies": { + "color-name": "^1.0.0" + } + }, + "node_modules/colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "node_modules/colormin": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/colormin/download/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", + "dev": true, + "dependencies": { + "color": "^0.11.0", + "css-color-names": "0.0.4", + "has": "^1.0.1" + } + }, + "node_modules/colors": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/colors/download/colors-1.1.2.tgz?cache=0&sync_timestamp=1589682043437&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcolors%2Fdownload%2Fcolors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "2.17.1", + "resolved": "https://registry.npm.taobao.org/commander/download/commander-2.17.1.tgz?cache=0&sync_timestamp=1598576136669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.17.1.tgz", + "integrity": "sha1-vXerfebelCBc6sxy8XFtKfIKd78=", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/commondir/download/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/component-emitter/download/component-emitter-1.3.0.tgz", + "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npm.taobao.org/compressible/download/compressible-2.0.18.tgz", + "integrity": "sha1-r1PMprBw1MPAdQ+9dyhqbXzEb7o=", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npm.taobao.org/compression/download/compression-1.7.4.tgz", + "integrity": "sha1-lVI+/xcMpXwpoMpB5v4TH0Hlu48=", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npm.taobao.org/concat-stream/download/concat-stream-1.6.2.tgz", + "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npm.taobao.org/connect-history-api-fallback/download/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha1-izIIk1kwjRERFdgcrT/Oq4iPl7w=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/console-browserify/download/console-browserify-1.2.0.tgz", + "integrity": "sha1-ZwY871fOts9Jk6KrOlWECujEkzY=", + "dev": true + }, + "node_modules/consolidate": { + "version": "0.14.5", + "resolved": "https://registry.npm.taobao.org/consolidate/download/consolidate-0.14.5.tgz?cache=0&sync_timestamp=1599596654038&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fconsolidate%2Fdownload%2Fconsolidate-0.14.5.tgz", + "integrity": "sha1-WiUEe8dvcwcmZ8jLUsmJiI9JTGM=", + "dev": true, + "dependencies": { + "bluebird": "^3.1.1" + } + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/constants-browserify/download/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.3.tgz", + "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npm.taobao.org/convert-source-map/download/convert-source-map-1.7.0.tgz", + "integrity": "sha1-F6LLiC1/d9NJBYXizmxSRCSjpEI=", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npm.taobao.org/cookie/download/cookie-0.4.0.tgz", + "integrity": "sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/copy-concurrently/download/copy-concurrently-1.0.5.tgz", + "integrity": "sha1-kilzmMrjSTf8r9bsgTnBgFHwteA=", + "dev": true, + "dependencies": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/copy-descriptor/download/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", + "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", + "dev": true, + "dependencies": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/core-js": { + "version": "2.6.11", + "resolved": "https://registry.npm.taobao.org/core-js/download/core-js-2.6.11.tgz", + "integrity": "sha1-OIMUafmSK97Y7iHJ3EaYXgOZMIw=", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npm.taobao.org/cosmiconfig/download/cosmiconfig-5.2.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcosmiconfig%2Fdownload%2Fcosmiconfig-5.2.1.tgz", + "integrity": "sha1-BA9yaAnFked6F8CjYmykW08Wixo=", + "dev": true, + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npm.taobao.org/esprima/download/esprima-4.0.1.tgz", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npm.taobao.org/js-yaml/download/js-yaml-3.14.0.tgz?cache=0&sync_timestamp=1590172281856&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-yaml%2Fdownload%2Fjs-yaml-3.14.0.tgz", + "integrity": "sha1-p6NBcPJqIbsWJCTYray0ETpp5II=", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npm.taobao.org/create-ecdh/download/create-ecdh-4.0.4.tgz?cache=0&sync_timestamp=1596557441827&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcreate-ecdh%2Fdownload%2Fcreate-ecdh-4.0.4.tgz", + "integrity": "sha1-1uf0v/pmc2CFoHYv06YyaE2rzE4=", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/create-hash/download/create-hash-1.2.0.tgz", + "integrity": "sha1-iJB4rxGmN1a8+1m9IhmWvjqe8ZY=", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npm.taobao.org/create-hmac/download/create-hmac-1.1.7.tgz", + "integrity": "sha1-aRcMeLOrlXFHsriwRXLkfq0iQ/8=", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npm.taobao.org/cross-spawn/download/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npm.taobao.org/crypto-browserify/download/crypto-browserify-3.12.0.tgz", + "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npm.taobao.org/css-color-names/download/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "engines": { + "node": ">4" + } + }, + "node_modules/css-declaration-sorter/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/css-declaration-sorter/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/css-loader": { + "version": "0.28.11", + "resolved": "https://registry.nlark.com/css-loader/download/css-loader-0.28.11.tgz?cache=0&sync_timestamp=1621865230592&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcss-loader%2Fdownload%2Fcss-loader-0.28.11.tgz", + "integrity": "sha1-w/mGSnAL4nEbtaJGKyOJsaOS2rc=", + "dev": true, + "dependencies": { + "babel-code-frame": "^6.26.0", + "css-selector-tokenizer": "^0.7.0", + "cssnano": "^3.10.0", + "icss-utils": "^2.1.0", + "loader-utils": "^1.0.2", + "lodash.camelcase": "^4.3.0", + "object-assign": "^4.1.1", + "postcss": "^5.0.6", + "postcss-modules-extract-imports": "^1.2.0", + "postcss-modules-local-by-default": "^1.2.0", + "postcss-modules-scope": "^1.1.0", + "postcss-modules-values": "^1.3.0", + "postcss-value-parser": "^3.3.0", + "source-list-map": "^2.0.0" + }, + "engines": { + "node": ">=0.12.0 || >= 4.3.0 < 5.0.0 || >=5.10" + } + }, + "node_modules/css-loader/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-loader/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-loader/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/css-loader/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-loader/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/css-loader/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-loader/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true + }, + "node_modules/css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npm.taobao.org/css-selector-tokenizer/download/css-selector-tokenizer-0.7.3.tgz?cache=0&sync_timestamp=1595335280942&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-selector-tokenizer%2Fdownload%2Fcss-selector-tokenizer-0.7.3.tgz", + "integrity": "sha1-c18mGG5nx0mq8nV4NAXPBmH66PE=", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/cssesc/download/cssesc-3.0.0.tgz", + "integrity": "sha1-N3QZGZA7hoVl4cCep0dEXNGJg+4=", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npm.taobao.org/cssnano/download/cssnano-3.10.0.tgz?cache=0&sync_timestamp=1599670481279&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcssnano%2Fdownload%2Fcssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "dev": true, + "dependencies": { + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" + } + }, + "node_modules/cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/cssnano-preset-default/node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, + "node_modules/cssnano-preset-default/node_modules/color-string": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/cssnano-preset-default/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/cssnano-preset-default/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cssnano-preset-default/node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/cssnano-preset-default/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-preset-default/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/cssnano-preset-default/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/cssnano-preset-default/node_modules/normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-calc/node_modules/postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "node_modules/cssnano-preset-default/node_modules/postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "dependencies": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-merge-rules/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-minify-selectors/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-preset-default/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cssnano-preset-default/node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/cssnano-util-raw-cache/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/cssnano/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano/node_modules/autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npm.taobao.org/autoprefixer/download/autoprefixer-6.7.7.tgz?cache=0&sync_timestamp=1601167517316&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fautoprefixer%2Fdownload%2Fautoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true, + "dependencies": { + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" + } + }, + "node_modules/cssnano/node_modules/browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "deprecated": "Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.", + "dev": true, + "dependencies": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + }, + "bin": { + "browserslist": "cli.js" + } + }, + "node_modules/cssnano/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cssnano/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/cssnano/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssnano/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/csso": { + "version": "2.3.2", + "resolved": "https://registry.npm.taobao.org/csso/download/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "dev": true, + "dependencies": { + "clap": "^1.0.9", + "source-map": "^0.5.3" + }, + "bin": { + "csso": "bin/csso" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cuint": { + "version": "0.2.2", + "resolved": "https://registry.npm.taobao.org/cuint/download/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", + "dev": true + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/currently-unhandled/download/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/cyclist/download/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/d/download/d-1.0.1.tgz", + "integrity": "sha1-hpgJU3LVjb7jRv/Qxwk/mfj561o=", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/de-indent/download/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1600502873540&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npm.taobao.org/decode-uri-component/download/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/deep-equal/download/deep-equal-1.1.1.tgz", + "integrity": "sha1-tcmMlCzv+vfLBR4k4UNKJaLmB2o=", + "dev": true, + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "node_modules/deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/define-properties/download/define-properties-1.1.3.tgz", + "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/defined/download/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "node_modules/del": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/del/download/del-3.0.0.tgz?cache=0&sync_timestamp=1601076806416&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdel%2Fdownload%2Fdel-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "dependencies": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/globby/download/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delegate": { + "version": "3.2.0", + "resolved": "https://registry.npm.taobao.org/delegate/download/delegate-3.2.0.tgz", + "integrity": "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=" + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/des.js/download/des.js-1.0.1.tgz", + "integrity": "sha1-U4IULhvcU/hdhtU+X0qn3rkeCEM=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/detect-indent/download/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/detect-node/download/detect-node-2.0.4.tgz", + "integrity": "sha1-AU7o+PZpxcWAI9pkuBecCDooxGw=", + "dev": true + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npm.taobao.org/diffie-hellman/download/diffie-hellman-5.0.3.tgz", + "integrity": "sha1-QOjumPVaIUlgcUaSHGPhrl89KHU=", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + }, + "node_modules/dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npm.taobao.org/dir-glob/download/dir-glob-2.2.2.tgz", + "integrity": "sha1-+gnwaUFTyJGLGLoN6vrpR2n8UMQ=", + "dev": true, + "dependencies": { + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/dns-equal/download/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/dns-txt/download/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npm.taobao.org/dom-converter/download/dom-converter-0.2.0.tgz", + "integrity": "sha1-ZyGp2u4uKTaClVtq/kFncWJ7t2g=", + "dev": true, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npm.taobao.org/dom-serializer/download/dom-serializer-0.2.2.tgz?cache=0&sync_timestamp=1600028888021&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdom-serializer%2Fdownload%2Fdom-serializer-0.2.2.tgz", + "integrity": "sha1-GvuB9TNxcXXUeGVd68XjMtn5u1E=", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/domelementtype/download/domelementtype-2.0.2.tgz?cache=0&sync_timestamp=1600028450905&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomelementtype%2Fdownload%2Fdomelementtype-2.0.2.tgz", + "integrity": "sha1-87blSSAeRvWItZRj3XcYcTH+aXE=", + "dev": true + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/domain-browser/download/domain-browser-1.2.0.tgz", + "integrity": "sha1-PTH1AZGmdJ3RN1p/Ui6CPULlTto=", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npm.taobao.org/domelementtype/download/domelementtype-1.3.1.tgz?cache=0&sync_timestamp=1600028450905&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomelementtype%2Fdownload%2Fdomelementtype-1.3.1.tgz", + "integrity": "sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=", + "dev": true + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/duplexer/download/duplexer-0.1.2.tgz", + "integrity": "sha1-Or5DrvODX4rgd9E23c4PJ2sEAOY=", + "dev": true + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npm.taobao.org/duplexify/download/duplexify-3.7.1.tgz", + "integrity": "sha1-Kk31MX9sz9kfhtb9JdjYoQO4gwk=", + "dev": true, + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/echarts": { + "version": "4.9.0", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-4.9.0.tgz", + "integrity": "sha512-+ugizgtJ+KmsJyyDPxaw2Br5FqzuBnyOWwcxPKO6y0gc5caYcfnEUIlNStx02necw8jmKmTafmpHhGo4XDtEIA==", + "dependencies": { + "zrender": "4.3.2" + } + }, + "node_modules/echarts-amap": { + "version": "1.0.0-rc.6", + "resolved": "https://registry.npmmirror.com/echarts-amap/-/echarts-amap-1.0.0-rc.6.tgz", + "integrity": "sha512-cYJCKoQdnkZXrGweYrveU1HruZd1c0KmsF1U8o3FtsvgR2jVL5ZUpGFjMmFtpolHOUFqxizk+s+QBLkYuOWL6Q==" + }, + "node_modules/echarts-liquidfill": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/echarts-liquidfill/-/echarts-liquidfill-2.0.6.tgz", + "integrity": "sha512-p+AH0O9/BtwXMQQyhjJbMZo+GwRAgWG/DCyK5r27PQzpS0UWrgXu57MyEFc0A8Ub3sRuqEu08BuxwHICBkSWSQ==", + "peerDependencies": { + "echarts": "^4.8.0", + "zrender": "^4.3.1" + } + }, + "node_modules/echarts-wordcloud": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/echarts-wordcloud/-/echarts-wordcloud-1.1.3.tgz", + "integrity": "sha512-Et8D5xEAoYkidmHun+hEH+2lF9dhCt6D0JJ390vlr2r/1zwhhZAbcL01CEvG93QcMcJpSvSPK8vRiGkTbMHRxg==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/ejs": { + "version": "2.7.4", + "resolved": "https://registry.npm.taobao.org/ejs/download/ejs-2.7.4.tgz?cache=0&sync_timestamp=1597678424776&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fejs%2Fdownload%2Fejs-2.7.4.tgz", + "integrity": "sha1-SGYSh1c9zFPjZsehrlLDoSDuybo=", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.3.742", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.742.tgz", + "integrity": "sha512-ihL14knI9FikJmH2XUIDdZFWJxvr14rPSdOhJ7PpS27xbz8qmaRwCwyg/bmFwjWKmWK9QyamiCZVCvXm5CH//Q==", + "dev": true + }, + "node_modules/element-ui": { + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.15.6.tgz", + "integrity": "sha512-rcYXEKd/j2G0AgficAOk1Zd1AsnHRkhmrK4yLHmNOiimU2JfsywgfKUjMoFuT6pQx0luhovj8lFjpE4Fnt58Iw==", + "dependencies": { + "async-validator": "~1.8.1", + "babel-helper-vue-jsx-merge-props": "^2.0.0", + "deepmerge": "^1.2.0", + "normalize-wheel": "^1.0.1", + "resize-observer-polyfill": "^1.5.0", + "throttle-debounce": "^1.0.1" + }, + "peerDependencies": { + "vue": "^2.5.17" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/emojis-list/download/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npm.taobao.org/end-of-stream/download/end-of-stream-1.4.4.tgz", + "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npm.taobao.org/enhanced-resolve/download/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/entities": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/entities/download/entities-2.0.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-2.0.3.tgz", + "integrity": "sha1-XEh+V0Krk8Fau12iJ1m4WQ7AO38=", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.7", + "resolved": "https://registry.npm.taobao.org/errno/download/errno-0.1.7.tgz", + "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npm.taobao.org/error-ex/download/error-ex-1.3.2.tgz", + "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.0.6", + "resolved": "https://registry.npm.taobao.org/error-stack-parser/download/error-stack-parser-2.0.6.tgz", + "integrity": "sha1-WpmnB716TFinl5AtSNgoA+3mqtg=", + "dev": true, + "dependencies": { + "stackframe": "^1.1.1" + } + }, + "node_modules/es-abstract": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.10.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npm.taobao.org/es-to-primitive/download/es-to-primitive-1.2.1.tgz", + "integrity": "sha1-5VzUyc3BiLzvsDs2bHNjI/xciYo=", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npm.taobao.org/es5-ext/download/es5-ext-0.10.53.tgz", + "integrity": "sha1-k8WjrP2+8nUiCtcmRK0C7hg2jeE=", + "dev": true, + "dependencies": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/es6-iterator/download/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npm.taobao.org/es6-map/download/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "node_modules/es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npm.taobao.org/es6-set/download/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + } + }, + "node_modules/es6-set/node_modules/es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/es6-symbol/download/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npm.taobao.org/es6-symbol/download/es6-symbol-3.1.3.tgz", + "integrity": "sha1-utXTwbzawoJp9MszHkMceKxwXRg=", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/es6-weak-map/download/es6-weak-map-2.0.3.tgz", + "integrity": "sha1-ttofFswswNm+Q+a9v8Xn383zHVM=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escope": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/escope/download/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "dev": true, + "dependencies": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npm.taobao.org/esprima/download/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/esrecurse/download/esrecurse-4.3.0.tgz", + "integrity": "sha1-eteWTWeauyi+5yzsY3WLHF0smSE=", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npm.taobao.org/estraverse/download/estraverse-5.2.0.tgz?cache=0&sync_timestamp=1596642941915&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Festraverse%2Fdownload%2Festraverse-5.2.0.tgz", + "integrity": "sha1-MH30JUfmzHMk088DwVXVzbjFOIA=", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/estraverse/download/estraverse-4.3.0.tgz?cache=0&sync_timestamp=1596642941915&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Festraverse%2Fdownload%2Festraverse-4.3.0.tgz", + "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/esutils/download/esutils-2.0.3.tgz", + "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npm.taobao.org/event-emitter/download/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npm.taobao.org/eventemitter3/download/eventemitter3-4.0.7.tgz?cache=0&sync_timestamp=1598517795415&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Feventemitter3%2Fdownload%2Feventemitter3-4.0.7.tgz", + "integrity": "sha1-Lem2j2Uo1WRO9cWVJqG0oHMGFp8=", + "dev": true + }, + "node_modules/events": { + "version": "3.2.0", + "resolved": "https://registry.npm.taobao.org/events/download/events-3.2.0.tgz?cache=0&sync_timestamp=1595422602348&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fevents%2Fdownload%2Fevents-3.2.0.tgz", + "integrity": "sha1-k7h8GPjvzUICpGGuxN/AVWtjk3k=", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "0.1.6", + "resolved": "https://registry.npm.taobao.org/eventsource/download/eventsource-0.1.6.tgz", + "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", + "dev": true, + "dependencies": { + "original": ">=0.0.5" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/evp_bytestokey/download/evp_bytestokey-1.0.3.tgz", + "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npm.taobao.org/execa/download/execa-0.7.0.tgz?cache=0&sync_timestamp=1594145111640&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fexeca%2Fdownload%2Fexeca-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npm.taobao.org/expand-brackets/download/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "version": "4.17.1", + "resolved": "https://registry.npm.taobao.org/express/download/express-4.17.1.tgz", + "integrity": "sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=", + "dev": true, + "dependencies": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/ext": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/ext/download/ext-1.4.0.tgz", + "integrity": "sha1-ia56BxWPedNVF4gpBDJAd+Q3kkQ=", + "dev": true, + "dependencies": { + "type": "^2.0.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/type/download/type-2.1.0.tgz?cache=0&sync_timestamp=1598016600310&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftype%2Fdownload%2Ftype-2.1.0.tgz", + "integrity": "sha1-m9wixkjPjPht0j0yM2pBz7ZHXj8=", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/is-extendable/download/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/extglob/download/extglob-2.0.4.tgz", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extract-text-webpack-plugin": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/extract-text-webpack-plugin/download/extract-text-webpack-plugin-3.0.2.tgz", + "integrity": "sha1-XwQ+qgL5dQqSWLeMCm4NwUCPsvc=", + "deprecated": "Deprecated. Please use https://github.com/webpack-contrib/mini-css-extract-plugin", + "dev": true, + "dependencies": { + "async": "^2.4.1", + "loader-utils": "^1.1.0", + "schema-utils": "^0.3.0", + "webpack-sources": "^1.0.1" + }, + "engines": { + "node": ">= 4.8 < 5.0.0 || >= 5.10" + }, + "peerDependencies": { + "webpack": "^3.1.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=", + "dev": true + }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/fastparse/download/fastparse-1.1.2.tgz", + "integrity": "sha1-kXKMWllC7O2FMSg8eUQe5BIsNak=", + "dev": true + }, + "node_modules/faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npm.taobao.org/faye-websocket/download/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npm.taobao.org/file-loader/download/file-loader-1.1.11.tgz", + "integrity": "sha1-b+iGRJsPKpNuQ8q6rAzb+zaVBvg=", + "dev": true, + "dependencies": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + }, + "engines": { + "node": ">= 4.3 < 5.0.0 || >= 5.10" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/file-loader/node_modules/ajv": { + "version": "6.12.5", + "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", + "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/file-loader/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", + "dev": true + }, + "node_modules/file-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "node_modules/file-loader/node_modules/schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", + "integrity": "sha1-unT1l9K+LqiAExdG7hfQoJPGgYc=", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/file-uri-to-path/download/file-uri-to-path-1.0.0.tgz", + "integrity": "sha1-VTp7hEb/b2hDWcRF8eN6BdrMM90=", + "dev": true, + "optional": true + }, + "node_modules/filesize": { + "version": "3.6.1", + "resolved": "https://registry.npm.taobao.org/filesize/download/filesize-3.6.1.tgz", + "integrity": "sha1-CQuz7gG2+AGoqL6Z0xcQs0Irsxc=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-7.0.1.tgz", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", + "dev": true, + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.2.tgz", + "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/find-cache-dir/download/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/find-up/download/find-up-2.1.0.tgz?cache=0&sync_timestamp=1597756298124&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-up%2Fdownload%2Ffind-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fingerprintjs2": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fingerprintjs2/-/fingerprintjs2-2.1.2.tgz", + "integrity": "sha512-ZPsLgjziFRbUb5tXWpEMtWp4XFnzSah8SiNfl3aoURDZ+2zi2tuIOYUULqDBV+Cb6paN+raWT+Q2qpOaCbX/Yw==", + "deprecated": "Package has been renamed to @fingerprintjs/fingerprintjs. Install @fingerprintjs/fingerprintjs to get updates." + }, + "node_modules/flatten": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/flatten/download/flatten-1.0.3.tgz", + "integrity": "sha1-wSg6yfJ7Noq8HjbR/3sEUBowNWs=", + "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash.", + "dev": true + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/flush-write-stream/download/flush-write-stream-1.1.1.tgz", + "integrity": "sha1-jdfYc6G6vCB9lOrQwuDkQnbr8ug=", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.13.0.tgz?cache=0&sync_timestamp=1597057997789&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.13.0.tgz", + "integrity": "sha1-tC6Nk6Kn7qXtiGM2dtZZe8jjhNs=", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/for-in/download/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/fragment-cache/download/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/friendly-errors-webpack-plugin": { + "version": "1.7.0", + "resolved": "https://registry.npm.taobao.org/friendly-errors-webpack-plugin/download/friendly-errors-webpack-plugin-1.7.0.tgz", + "integrity": "sha1-78hsu4FiJFZYYaG+ep2E0Kr+oTY=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "error-stack-parser": "^2.0.0", + "string-width": "^2.0.0" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/friendly-errors-webpack-plugin/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/friendly-errors-webpack-plugin/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/friendly-errors-webpack-plugin/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/from2/download/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npm.taobao.org/fs-write-stream-atomic/download/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-2.1.3.tgz", + "integrity": "sha1-+3OHA66NL5/pAMM4Nt3r7ouX8j4=", + "deprecated": "\"Please update to latest v2.3 or v2.2\"", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", + "dev": true + }, + "node_modules/geotiff": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/geotiff/-/geotiff-2.0.5.tgz", + "integrity": "sha512-U5kVYm118YAmw2swiLu8rhfrYnDKOFI7VaMjuQwcq6Intuuid9Pyb4jjxYUxxkq8kOu2r7Am0Rmb52PObGp4pQ==", + "dependencies": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.0", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2" + }, + "engines": { + "node": ">=10.19" + } + }, + "node_modules/geotiff/node_modules/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==" + }, + "node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/get-caller-file/download/get-caller-file-1.0.3.tgz", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", + "dev": true + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npm.taobao.org/get-stdin/download/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/get-stream/download/get-stream-3.0.0.tgz?cache=0&sync_timestamp=1597056464385&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fget-stream%2Fdownload%2Fget-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npm.taobao.org/get-value/download/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npm.taobao.org/glob/download/glob-7.1.6.tgz", + "integrity": "sha1-FB8zuBp8JJLhJVlDB0gMRmeSeKY=", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "optional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "9.18.0", + "resolved": "https://registry.npm.taobao.org/globals/download/globals-9.18.0.tgz?cache=0&sync_timestamp=1596709369054&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobals%2Fdownload%2Fglobals-9.18.0.tgz", + "integrity": "sha1-qjiWs+abSH8X4x7SFD1pqOMMLYo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby": { + "version": "7.1.1", + "resolved": "https://registry.npm.taobao.org/globby/download/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npm.taobao.org/good-listener/download/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "dependencies": { + "delegate": "^3.1.2" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.2.4.tgz", + "integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs=", + "dev": true + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/growly/download/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "node_modules/gzip-size": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/gzip-size/download/gzip-size-4.1.0.tgz", + "integrity": "sha1-iuCWJX6r59acRb4rZ8RIEk/7UXw=", + "dev": true, + "dependencies": { + "duplexer": "^0.1.1", + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/handle-thing/download/handle-thing-2.0.1.tgz", + "integrity": "sha1-hX95zjWVgMNA1DCBzGSJcNC7I04=", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/has/download/has-1.0.3.tgz", + "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/has-ansi/download/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-value/download/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-values/download/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/hash-base/download/hash-base-3.1.0.tgz", + "integrity": "sha1-VcOB2eBuHSmXqIO0o/3f5/DTrzM=", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.1.tgz", + "integrity": "sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=", + "dev": true + }, + "node_modules/hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/hash-sum/download/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npm.taobao.org/hash.js/download/hash.js-1.1.7.tgz", + "integrity": "sha1-C6vKU46NTuSg+JiNaIZlN6ADz0I=", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/he/download/he-1.2.0.tgz", + "integrity": "sha1-hK5l+n6vsWX922FWauFLrwVmTw8=", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/hmac-drbg/download/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/home-or-tmp/download/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npm.taobao.org/hpack.js/download/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "node_modules/hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "node_modules/html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/html-comment-regex/download/html-comment-regex-1.1.2.tgz", + "integrity": "sha1-l9RoiutcgYhqNk+qDK0d2hTUM6c=", + "dev": true + }, + "node_modules/html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npm.taobao.org/html-entities/download/html-entities-1.3.1.tgz", + "integrity": "sha1-+5oaS1sUxdq6gtPjTGrk/nAaDkQ=", + "dev": true + }, + "node_modules/html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npm.taobao.org/html-minifier/download/html-minifier-3.5.21.tgz", + "integrity": "sha1-0AQOBUcw41TbAIRjWTGUAVIS0gw=", + "dev": true, + "dependencies": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/html-webpack-plugin": { + "version": "2.30.1", + "resolved": "https://registry.npm.taobao.org/html-webpack-plugin/download/html-webpack-plugin-2.30.1.tgz?cache=0&sync_timestamp=1600690506465&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-2.30.1.tgz", + "integrity": "sha1-f5xCG36pHsRg9WUn1430hO51N9U=", + "deprecated": "out of support", + "dev": true, + "dependencies": { + "bluebird": "^3.4.7", + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "toposort": "^1.0.0" + }, + "peerDependencies": { + "webpack": "1 || ^2 || ^2.1.0-beta || ^2.2.0-rc || ^3" + } + }, + "node_modules/html-webpack-plugin/node_modules/big.js": { + "version": "3.2.0", + "resolved": "https://registry.npm.taobao.org/big.js/download/big.js-3.2.0.tgz", + "integrity": "sha1-pfwpi4G54Nyi5FiCR4S2XFK6WI4=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/html-webpack-plugin/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/emojis-list/download/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/html-webpack-plugin/node_modules/loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "dependencies": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npm.taobao.org/http-deceiver/download/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.2.tgz?cache=0&sync_timestamp=1593407611415&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-errors%2Fdownload%2Fhttp-errors-1.7.2.tgz", + "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/http-parser-js": { + "version": "0.5.2", + "resolved": "https://registry.npm.taobao.org/http-parser-js/download/http-parser-js-0.5.2.tgz", + "integrity": "sha1-2i4x0jezk6rnKs5DiC3X4nCo/3c=", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npm.taobao.org/http-proxy/download/http-proxy-1.18.1.tgz", + "integrity": "sha1-QBVB8FNIhLv5UmAzTnL4juOXZUk=", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "0.19.2", + "resolved": "https://registry.npm.taobao.org/http-proxy-middleware/download/http-proxy-middleware-0.19.2.tgz", + "integrity": "sha1-7nPcyDSBZa/v6N4v9xd1HRgWCO4=", + "dev": true, + "dependencies": { + "http-proxy": "^1.18.1", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/https-browserify/download/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz?cache=0&sync_timestamp=1594184325364&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ficonv-lite%2Fdownload%2Ficonv-lite-0.4.24.tgz", + "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/icss-replace-symbols/download/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "node_modules/icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/icss-utils/download/icss-utils-2.1.0.tgz?cache=0&sync_timestamp=1600767333663&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ficss-utils%2Fdownload%2Ficss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "dev": true, + "dependencies": { + "postcss": "^6.0.1" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npm.taobao.org/ieee754/download/ieee754-1.1.13.tgz", + "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=" + }, + "node_modules/iferr": { + "version": "0.1.5", + "resolved": "https://registry.npm.taobao.org/iferr/download/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npm.taobao.org/ignore/download/ignore-3.3.10.tgz?cache=0&sync_timestamp=1590809380232&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fignore%2Fdownload%2Fignore-3.3.10.tgz", + "integrity": "sha1-Cpf7h2mG6AgcYxFg+PnziRV/AEM=", + "dev": true + }, + "node_modules/import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/import-cwd/download/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "dependencies": { + "import-from": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/import-fresh/download/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/import-from/download/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/import-local/download/import-local-1.0.0.tgz", + "integrity": "sha1-Xk/9wD9P5sAJxnKb6yljHC+CJ7w=", + "dev": true, + "dependencies": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npm.taobao.org/imurmurhash/download/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/indent-string/download/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/indexes-of/download/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz", + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=", + "dev": true + }, + "node_modules/internal-ip": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/internal-ip/download/internal-ip-1.2.0.tgz?cache=0&sync_timestamp=1596563415126&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finternal-ip%2Fdownload%2Finternal-ip-1.2.0.tgz", + "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", + "dev": true, + "dependencies": { + "meow": "^3.3.0" + }, + "bin": { + "internal-ip": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/interpret/download/interpret-1.4.0.tgz", + "integrity": "sha1-Zlq4vE2iendKQFhOgS4+D6RbGh4=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npm.taobao.org/invariant/download/invariant-2.2.4.tgz", + "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/invert-kv/download/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npm.taobao.org/ip/download/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npm.taobao.org/ipaddr.js/download/ipaddr.js-1.9.1.tgz", + "integrity": "sha1-v/OFQ+64mEglB5/zoqjmy9RngbM=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/is-absolute-url/download/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/is-arguments/download/is-arguments-1.0.4.tgz", + "integrity": "sha1-P6+WbHy6D/Q3+zH2JQCC/PBEjPM=", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/is-arrayish/download/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/is-binary-path/download/is-binary-path-2.1.0.tgz", + "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=", + "dev": true, + "optional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npm.taobao.org/is-buffer/download/is-buffer-1.1.6.tgz", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "dev": true + }, + "node_modules/is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "dependencies": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-date-object/download/is-date-object-1.0.2.tgz", + "integrity": "sha1-vac28s2P0G0yhE53Q7+nSUw7/X4=", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-0.1.6.tgz", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-5.1.0.tgz", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npm.taobao.org/is-directory/download/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/is-extendable/download/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/is-extglob/download/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/is-finite/download/is-finite-1.1.0.tgz", + "integrity": "sha1-kEE1x3+0LAZB1qobzbxNqo2ggvM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-4.0.1.tgz", + "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-7.0.0.tgz", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-path-cwd/download/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/is-path-in-cwd/download/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha1-WsSLNF72dTOb1sekipEhELJBz1I=", + "dev": true, + "dependencies": { + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/is-path-inside/download/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/is-plain-obj/download/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/is-plain-object/download/is-plain-object-2.0.4.tgz?cache=0&sync_timestamp=1599667279942&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-plain-object%2Fdownload%2Fis-plain-object-2.0.4.tgz", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/is-resolvable/download/is-resolvable-1.1.0.tgz", + "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", + "dev": true + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/is-stream/download/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/is-svg/download/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "dev": true, + "dependencies": { + "html-comment-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/is-symbol/download/is-symbol-1.0.3.tgz", + "integrity": "sha1-OOEBS55jKb4N6dJKQU/XRB7GGTc=", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/is-utf8/download/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-windows/download/is-windows-1.0.2.tgz", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/is-wsl/download/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/isobject/download/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, + "node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npm.taobao.org/js-base64/download/js-base64-2.6.4.tgz?cache=0&sync_timestamp=1599897619557&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-base64%2Fdownload%2Fjs-base64-2.6.4.tgz", + "integrity": "sha1-9OaGxd4eofhn28rT1G2WlCjfmMQ=", + "dev": true + }, + "node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/js-tokens/download/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/jsesc/download/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/json-loader/download/json-loader-0.5.7.tgz", + "integrity": "sha1-3KFKcCNf+C8KyaOr62DTN6NlGF0=", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/json-parse-better-errors/download/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.3.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "node_modules/json-stringify-pretty-compact": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz", + "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==" + }, + "node_modules/json3": { + "version": "3.3.3", + "resolved": "https://registry.npm.taobao.org/json3/download/json3-3.3.3.tgz", + "integrity": "sha1-f8EON1/FrkLEcFpcwKpvYr4wW4E=", + "dev": true + }, + "node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/killable/download/killable-1.0.1.tgz", + "integrity": "sha1-TIzkQRh6Bhx0dPuHygjipjgZSJI=", + "dev": true + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/last-call-webpack-plugin": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/last-call-webpack-plugin/download/last-call-webpack-plugin-2.1.2.tgz", + "integrity": "sha1-rYDG4xCZgpTS7SGApo6VieR2jEQ=", + "dev": true, + "dependencies": { + "lodash": "^4.17.4", + "webpack-sources": "^1.0.1" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/lazy-cache/download/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/lcid/download/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==" + }, + "node_modules/load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/load-json-file/download/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz?cache=0&sync_timestamp=1598130878813&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npm.taobao.org/loader-runner/download/loader-runner-2.4.0.tgz?cache=0&sync_timestamp=1601450715716&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-runner%2Fdownload%2Floader-runner-2.4.0.tgz", + "integrity": "sha1-7UcGa/5TTX6ExMe5mYwqdWB9k1c=", + "dev": true, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-1.4.0.tgz", + "integrity": "sha1-xXm140yzSxp07cbB+za/o3HVphM=", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/json5/download/json5-1.0.1.tgz", + "integrity": "sha1-d5+wAYYE+oVOrL9iUhgNg1Q+Pb4=", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/locate-path/download/locate-path-2.0.0.tgz?cache=0&sync_timestamp=1597081904643&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flocate-path%2Fdownload%2Flocate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npm.taobao.org/lodash.camelcase/download/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npm.taobao.org/lodash.memoize/download/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npm.taobao.org/lodash.uniq/download/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/log-symbols/download/log-symbols-2.2.0.tgz?cache=0&sync_timestamp=1589682056270&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flog-symbols%2Fdownload%2Flog-symbols-2.2.0.tgz", + "integrity": "sha1-V0Dhxdbw39pK2TI7UzIQfva0xAo=", + "dev": true, + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/loglevel": { + "version": "1.7.0", + "resolved": "https://registry.npm.taobao.org/loglevel/download/loglevel-1.7.0.tgz?cache=0&sync_timestamp=1598447642950&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Floglevel%2Fdownload%2Floglevel-1.7.0.tgz", + "integrity": "sha1-coFmhVp0DVnTjbAc9G8ELKoEG7A=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/longest": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/longest/download/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/loose-envify/download/loose-envify-1.4.0.tgz", + "integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npm.taobao.org/loud-rejection/download/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npm.taobao.org/lower-case/download/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npm.taobao.org/lru-cache/download/lru-cache-4.1.5.tgz?cache=0&sync_timestamp=1594427519396&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flru-cache%2Fdownload%2Flru-cache-4.1.5.tgz", + "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/make-dir/download/make-dir-1.3.0.tgz", + "integrity": "sha1-ecEDO4BRW9bSTsmTPoYMp17ifww=", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npm.taobao.org/map-cache/download/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/map-obj/download/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/map-visit/download/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mapbox-to-css-font": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/mapbox-to-css-font/-/mapbox-to-css-font-2.4.1.tgz", + "integrity": "sha512-QQ/iKiM43DM9+aujTL45Iz5o7gDeSFmy4LPl3HZmNcwCE++NxGazf+yFpY+wCb+YS23sDa1ghpo3zrNFOcHlow==" + }, + "node_modules/math-expression-evaluator": { + "version": "1.3.8", + "resolved": "https://registry.nlark.com/math-expression-evaluator/download/math-expression-evaluator-1.3.8.tgz", + "integrity": "sha1-Mg2jsrwVEvT1D8MCCysc1cjp1Xc=", + "dev": true + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npm.taobao.org/md5.js/download/md5.js-1.3.5.tgz", + "integrity": "sha1-tdB7jjIW4+J81yjXL3DR5qNCAF8=", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/mem/download/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/memory-fs/download/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/meow": { + "version": "3.7.0", + "resolved": "https://registry.npm.taobao.org/meow/download/meow-3.7.0.tgz?cache=0&sync_timestamp=1598693287069&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmeow%2Fdownload%2Fmeow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "dependencies": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/find-up/download/find-up-1.1.2.tgz?cache=0&sync_timestamp=1597756298124&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-up%2Fdownload%2Ffind-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/load-json-file/download/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz?cache=0&sync_timestamp=1598130878813&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/path-exists/download/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/path-type/download/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/read-pkg/download/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/read-pkg-up/download/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/strip-bom/download/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npm.taobao.org/micromatch/download/micromatch-3.1.10.tgz", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npm.taobao.org/braces/download/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/micromatch/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npm.taobao.org/miller-rabin/download/miller-rabin-4.0.1.tgz", + "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz", + "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npm.taobao.org/mime-db/download/mime-db-1.44.0.tgz?cache=0&sync_timestamp=1600831212519&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-db%2Fdownload%2Fmime-db-1.44.0.tgz", + "integrity": "sha1-+hHF6wrKEzS0Izy01S8QxaYnL5I=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.27.tgz", + "integrity": "sha1-R5SfmOJ56lMRn1ci4PNOUpvsAJ8=", + "dev": true, + "dependencies": { + "mime-db": "1.44.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/mimic-fn/download/mimic-fn-1.2.0.tgz?cache=0&sync_timestamp=1596094012686&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmimic-fn%2Fdownload%2Fmimic-fn-1.2.0.tgz", + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/minimalistic-assert/download/minimalistic-assert-1.0.1.tgz", + "integrity": "sha1-LhlN4ERibUoQ5/f7wAznPoPk1cc=", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/minimalistic-crypto-utils/download/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npm.taobao.org/minimist/download/minimist-1.2.5.tgz", + "integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=" + }, + "node_modules/mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/mississippi/download/mississippi-2.0.0.tgz", + "integrity": "sha1-NEKlCPr8KFAEhv7qmUCWduTuWm8=", + "dev": true, + "dependencies": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npm.taobao.org/mixin-deep/download/mixin-deep-1.3.2.tgz", + "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/is-extendable/download/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.5.tgz", + "integrity": "sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npm.taobao.org/moment/download/moment-2.29.1.tgz?cache=0&sync_timestamp=1601983423917&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmoment%2Fdownload%2Fmoment-2.29.1.tgz", + "integrity": "sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M=", + "engines": { + "node": "*" + } + }, + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "dependencies": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npm.taobao.org/multicast-dns/download/multicast-dns-6.2.3.tgz", + "integrity": "sha1-oOx72QVcQoL3kMPIL04o2zsxsik=", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/multicast-dns-service-types/download/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "node_modules/nan": { + "version": "2.14.1", + "resolved": "https://registry.npm.taobao.org/nan/download/nan-2.14.1.tgz", + "integrity": "sha1-174036MQW5FJTDFHCJMV7/iHSwE=", + "dev": true, + "optional": true + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npm.taobao.org/nanomatch/download/nanomatch-1.2.13.tgz", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nanomatch/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npm.taobao.org/negotiator/download/negotiator-0.6.2.tgz", + "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npm.taobao.org/neo-async/download/neo-async-2.6.2.tgz?cache=0&sync_timestamp=1594317437265&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fneo-async%2Fdownload%2Fneo-async-2.6.2.tgz", + "integrity": "sha1-tKr7k+OustgXTKU88WOrfXMIMF8=", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/next-tick/download/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npm.taobao.org/no-case/download/no-case-2.3.2.tgz", + "integrity": "sha1-YLgTOWvjmz8SiKTB7V0efSi0ZKw=", + "dev": true, + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npm.taobao.org/node-forge/download/node-forge-0.10.0.tgz?cache=0&sync_timestamp=1599010730714&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-forge%2Fdownload%2Fnode-forge-0.10.0.tgz", + "integrity": "sha1-Mt6ir7Ppkm8C7lzoeUkCaRpna/M=", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/node-libs-browser/download/node-libs-browser-2.2.1.tgz", + "integrity": "sha1-tk9RPRgzhiX5A0bSew0jXmMfZCU=", + "dev": true, + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "node_modules/node-notifier": { + "version": "5.4.3", + "resolved": "https://registry.npm.taobao.org/node-notifier/download/node-notifier-5.4.3.tgz?cache=0&sync_timestamp=1597311297466&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-notifier%2Fdownload%2Fnode-notifier-5.4.3.tgz", + "integrity": "sha1-y3La+UyTkECY4oucWQ/YZuRkvVA=", + "dev": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "node_modules/node-releases": { + "version": "1.1.72", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", + "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npm.taobao.org/normalize-package-data/download/normalize-package-data-2.5.0.tgz", + "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/normalize-path/download/normalize-path-3.0.0.tgz", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/normalize-range/download/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npm.taobao.org/normalize-url/download/normalize-url-1.9.1.tgz?cache=0&sync_timestamp=1601463025217&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnormalize-url%2Fdownload%2Fnormalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "dependencies": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/normalize-wheel/download/normalize-wheel-1.0.1.tgz", + "integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU=" + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/npm-run-path/download/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/nth-check/download/nth-check-1.0.2.tgz", + "integrity": "sha1-sr0pXDfj3VijvwcAN2Zjuk2c8Fw=", + "dev": true, + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npm.taobao.org/num2fraction/download/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/numerify": { + "version": "1.2.9", + "resolved": "https://registry.npmmirror.com/numerify/-/numerify-1.2.9.tgz", + "integrity": "sha512-X4QzQiytV5ZN3TVLhzbtFzjTarUNnaa1pgNDFqt7u7Nqhxe7FvY2eYrGt4WYHlYXDqgtfC/n/a5nJ2y0LijV8w==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npm.taobao.org/object-copy/download/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true + }, + "node_modules/object-is": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/object-is/download/object-is-1.1.3.tgz?cache=0&sync_timestamp=1601503177879&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fobject-is%2Fdownload%2Fobject-is-1.1.3.tgz", + "integrity": "sha1-LjueZVYBN0Ve471irsTZCi6hzIE=", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/object-keys/download/object-keys-1.1.1.tgz", + "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/object-visit/download/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.nlark.com/object.assign/download/object.assign-4.1.2.tgz", + "integrity": "sha1-DtVKNC7Os3s4/3brgxoOeIy2OUA=", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/object.pick/download/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/obuf/download/obuf-1.1.2.tgz", + "integrity": "sha1-Cb6jND1BhZ69RGKS0RydTbYZCE4=", + "dev": true + }, + "node_modules/ol": { + "version": "6.14.1", + "resolved": "https://registry.npmmirror.com/ol/-/ol-6.14.1.tgz", + "integrity": "sha512-sIcUWkGud3Y2gT3TJubSHlkyMXiPVh1yxfCPHxmY8+qtm79bB9oRnei9xHVIbRRG0Ro6Ldp5E+BMVSvYCxSpaA==", + "dependencies": { + "geotiff": "^2.0.2", + "ol-mapbox-style": "^7.1.1", + "pbf": "3.2.1", + "rbush": "^3.0.1" + } + }, + "node_modules/ol-mapbox-style": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/ol-mapbox-style/-/ol-mapbox-style-7.1.1.tgz", + "integrity": "sha512-GLTEYiH/Ec9Zn1eS4S/zXyR2sierVrUc+OLVP8Ra0FRyqRhoYbXdko0b7OIeSHWdtJfHssWYefDOGxfTRUUZ/A==", + "dependencies": { + "@mapbox/mapbox-gl-style-spec": "^13.20.1", + "mapbox-to-css-font": "^2.4.1", + "webfont-matcher": "^1.1.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/on-headers/download/on-headers-1.0.2.tgz", + "integrity": "sha1-dysK5qqlJcOZ5Imt+tkMQD6zwo8=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/once/download/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/onetime/download/onetime-2.0.1.tgz?cache=0&sync_timestamp=1597005190531&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fonetime%2Fdownload%2Fonetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npm.taobao.org/opener/download/opener-1.5.2.tgz?cache=0&sync_timestamp=1598733310448&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fopener%2Fdownload%2Fopener-1.5.2.tgz", + "integrity": "sha1-XTfh81B3udysQwE3InGv3rKhNZg=", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npm.taobao.org/opn/download/opn-5.5.0.tgz", + "integrity": "sha1-/HFk+rVtI1kExRw7J9pnWMo7m/w=", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optimize-css-assets-webpack-plugin": { + "version": "3.2.1", + "resolved": "https://registry.npm.taobao.org/optimize-css-assets-webpack-plugin/download/optimize-css-assets-webpack-plugin-3.2.1.tgz?cache=0&sync_timestamp=1598800822300&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Foptimize-css-assets-webpack-plugin%2Fdownload%2Foptimize-css-assets-webpack-plugin-3.2.1.tgz", + "integrity": "sha1-nRhlSg4FjAkL3ZkbBLyw9vJIZXM=", + "dev": true, + "dependencies": { + "cssnano": "^4.1.10", + "last-call-webpack-plugin": "^2.1.2" + } + }, + "node_modules/optimize-css-assets-webpack-plugin/node_modules/cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "dev": true, + "dependencies": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/optimize-css-assets-webpack-plugin/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/optimize-css-assets-webpack-plugin/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-6.1.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-6.1.0.tgz", + "integrity": "sha1-B2Srxpxj1ayELdSGfo0CXogN+PM=", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ora": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/ora/download/ora-1.4.0.tgz?cache=0&sync_timestamp=1599423139882&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fora%2Fdownload%2Fora-1.4.0.tgz", + "integrity": "sha1-iERYIVs6XUCXWSKF+TMhu3p54uU=", + "dev": true, + "dependencies": { + "chalk": "^2.1.0", + "cli-cursor": "^2.1.0", + "cli-spinners": "^1.0.1", + "log-symbols": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/original": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/original/download/original-1.0.2.tgz", + "integrity": "sha1-5EKmHP/hxf0gpl8yYcJmY7MD8l8=", + "dev": true, + "dependencies": { + "url-parse": "^1.4.3" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/os-browserify/download/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/os-homedir/download/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "dependencies": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/os-tmpdir/download/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/p-finally/download/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/p-limit/download/p-limit-1.3.0.tgz?cache=0&sync_timestamp=1594559720897&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fp-limit%2Fdownload%2Fp-limit-1.3.0.tgz", + "integrity": "sha1-uGvV8MJWkJEcdZD8v8IBDVSzzLg=", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/p-locate/download/p-locate-2.0.0.tgz?cache=0&sync_timestamp=1597081785924&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fp-locate%2Fdownload%2Fp-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/p-map/download/p-map-1.2.0.tgz", + "integrity": "sha1-5OlPMR6rvIYzoeeZCBZfyiYkG2s=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/p-try/download/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npm.taobao.org/pako/download/pako-1.0.11.tgz", + "integrity": "sha1-bJWZ00DVTf05RjgCUqNXBaa5kr8=", + "dev": true + }, + "node_modules/parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/parallel-transform/download/parallel-transform-1.2.0.tgz", + "integrity": "sha1-kEnKN9bLIYLDsdLHIL6U0UpYFPw=", + "dev": true, + "dependencies": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/param-case/download/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npm.taobao.org/parse-asn1/download/parse-asn1-5.1.6.tgz", + "integrity": "sha1-OFCAo+wTy2KmLTlAnLPoiETNrtQ=", + "dev": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==" + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-4.0.0.tgz?cache=0&sync_timestamp=1598130878813&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz", + "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/pascalcase/download/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npm.taobao.org/path-browserify/download/path-browserify-0.0.1.tgz", + "integrity": "sha1-5sTd1+06onxoogzE5Q4aTug7vEo=", + "dev": true + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/path-dirname/download/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/path-exists/download/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/path-is-inside/download/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/path-key/download/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpath-to-regexp%2Fdownload%2Fpath-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/path-type/download/path-type-3.0.0.tgz", + "integrity": "sha1-zvMdyOCho7sNEFwM2Xzzv0f0428=", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/pbkdf2/download/pbkdf2-3.1.1.tgz", + "integrity": "sha1-y4cksPramEWWhW0abrr9NYRlS5Q=", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npm.taobao.org/picomatch/download/picomatch-2.2.2.tgz", + "integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=", + "dev": true, + "optional": true, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/pinkie/download/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/pinkie-promise/download/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/pkg-dir/download/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npm.taobao.org/portfinder/download/portfinder-1.0.28.tgz?cache=0&sync_timestamp=1596018172434&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fportfinder%2Fdownload%2Fportfinder-1.0.28.tgz", + "integrity": "sha1-Z8RiKFK9U3TdHdkA93n1NGL6x3g=", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz?cache=0&sync_timestamp=1600502873540&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.2.6.tgz", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/posix-character-classes/download/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-6.0.23.tgz", + "integrity": "sha1-YcgswyisYOZ3ZF+XkFTrmLwOMyQ=", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npm.taobao.org/postcss-calc/download/postcss-calc-5.3.1.tgz?cache=0&sync_timestamp=1601732624081&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-calc%2Fdownload%2Fpostcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "dev": true, + "dependencies": { + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" + } + }, + "node_modules/postcss-calc/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-calc/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-calc/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-calc/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-calc/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-calc/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-calc/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npm.taobao.org/postcss-colormin/download/postcss-colormin-2.2.2.tgz?cache=0&sync_timestamp=1599670485775&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-colormin%2Fdownload%2Fpostcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "dev": true, + "dependencies": { + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" + } + }, + "node_modules/postcss-colormin/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-colormin/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-colormin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-colormin/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npm.taobao.org/postcss-convert-values/download/postcss-convert-values-2.6.1.tgz?cache=0&sync_timestamp=1599674174819&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-convert-values%2Fdownload%2Fpostcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "dev": true, + "dependencies": { + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" + } + }, + "node_modules/postcss-convert-values/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-convert-values/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-convert-values/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-convert-values/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-convert-values/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-convert-values/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npm.taobao.org/postcss-discard-comments/download/postcss-discard-comments-2.0.4.tgz?cache=0&sync_timestamp=1599674175141&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-discard-comments%2Fdownload%2Fpostcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "dev": true, + "dependencies": { + "postcss": "^5.0.14" + } + }, + "node_modules/postcss-discard-comments/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-discard-comments/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-comments/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/postcss-discard-duplicates/download/postcss-discard-duplicates-2.1.0.tgz?cache=0&sync_timestamp=1599674175412&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-discard-duplicates%2Fdownload%2Fpostcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-duplicates/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/postcss-discard-empty/download/postcss-discard-empty-2.1.0.tgz?cache=0&sync_timestamp=1599670482017&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-discard-empty%2Fdownload%2Fpostcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "dev": true, + "dependencies": { + "postcss": "^5.0.14" + } + }, + "node_modules/postcss-discard-empty/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-discard-empty/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-empty/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/postcss-discard-overridden/download/postcss-discard-overridden-0.1.1.tgz?cache=0&sync_timestamp=1599670482109&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-discard-overridden%2Fdownload%2Fpostcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "dev": true, + "dependencies": { + "postcss": "^5.0.16" + } + }, + "node_modules/postcss-discard-overridden/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-discard-overridden/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-overridden/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npm.taobao.org/postcss-discard-unused/download/postcss-discard-unused-2.2.3.tgz?cache=0&sync_timestamp=1599672334828&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-discard-unused%2Fdownload%2Fpostcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "dev": true, + "dependencies": { + "postcss": "^5.0.14", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss-discard-unused/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-unused/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-unused/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-discard-unused/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-unused/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-discard-unused/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-discard-unused/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-filter-plugins": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/postcss-filter-plugins/download/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha1-giRf34IzcEFkXkdxFNjlk6oYuOw=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + } + }, + "node_modules/postcss-filter-plugins/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-filter-plugins/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-filter-plugins/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-filter-plugins/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-filter-plugins/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-filter-plugins/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-filter-plugins/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-import": { + "version": "11.1.0", + "resolved": "https://registry.npm.taobao.org/postcss-import/download/postcss-import-11.1.0.tgz", + "integrity": "sha1-Vck2LJGSmU7GiGXSJEGd8dspgfA=", + "dev": true, + "dependencies": { + "postcss": "^6.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "node_modules/postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/postcss-load-config/download/postcss-load-config-2.1.2.tgz?cache=0&sync_timestamp=1601607668112&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-load-config%2Fdownload%2Fpostcss-load-config-2.1.2.tgz", + "integrity": "sha1-xepQTyxK7zPHNZo03jVzdyrXUCo=", + "dev": true, + "dependencies": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/postcss-load-options": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/postcss-load-options/download/postcss-load-options-1.2.0.tgz", + "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", + "dev": true, + "dependencies": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-options/node_modules/cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npm.taobao.org/cosmiconfig/download/cosmiconfig-2.2.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcosmiconfig%2Fdownload%2Fcosmiconfig-2.2.2.tgz", + "integrity": "sha1-YXPOvVb6wELB9DkO33r2wHx8uJI=", + "dev": true, + "dependencies": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-options/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz?cache=0&sync_timestamp=1598130878813&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-load-plugins": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/postcss-load-plugins/download/postcss-load-plugins-2.3.0.tgz", + "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", + "dev": true, + "dependencies": { + "cosmiconfig": "^2.1.1", + "object-assign": "^4.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-plugins/node_modules/cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npm.taobao.org/cosmiconfig/download/cosmiconfig-2.2.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcosmiconfig%2Fdownload%2Fcosmiconfig-2.2.2.tgz", + "integrity": "sha1-YXPOvVb6wELB9DkO33r2wHx8uJI=", + "dev": true, + "dependencies": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-load-plugins/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz?cache=0&sync_timestamp=1598130878813&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-loader": { + "version": "2.1.6", + "resolved": "https://registry.npm.taobao.org/postcss-loader/download/postcss-loader-2.1.6.tgz", + "integrity": "sha1-HX3XsXxrojS5vtWvE+C+pApC10A=", + "dev": true, + "dependencies": { + "loader-utils": "^1.1.0", + "postcss": "^6.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^0.4.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/postcss-loader/node_modules/ajv": { + "version": "6.12.5", + "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", + "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/postcss-loader/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", + "dev": true + }, + "node_modules/postcss-loader/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", + "integrity": "sha1-unT1l9K+LqiAExdG7hfQoJPGgYc=", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npm.taobao.org/postcss-merge-idents/download/postcss-merge-idents-2.1.7.tgz?cache=0&sync_timestamp=1599672336407&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-merge-idents%2Fdownload%2Fpostcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "dev": true, + "dependencies": { + "has": "^1.0.1", + "postcss": "^5.0.10", + "postcss-value-parser": "^3.1.1" + } + }, + "node_modules/postcss-merge-idents/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-idents/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-idents/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-merge-idents/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-idents/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-merge-idents/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-idents/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/postcss-merge-longhand/download/postcss-merge-longhand-2.0.2.tgz?cache=0&sync_timestamp=1599670482411&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-merge-longhand%2Fdownload%2Fpostcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + } + }, + "node_modules/postcss-merge-longhand/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-merge-longhand/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-longhand/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/postcss-merge-rules/download/postcss-merge-rules-2.1.2.tgz?cache=0&sync_timestamp=1599670482522&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-merge-rules%2Fdownload%2Fpostcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "dev": true, + "dependencies": { + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npm.taobao.org/browserslist/download/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "deprecated": "Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.", + "dev": true, + "dependencies": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + }, + "bin": { + "browserslist": "cli.js" + } + }, + "node_modules/postcss-merge-rules/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-merge-rules/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-merge-rules/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/postcss-message-helpers/download/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", + "dev": true + }, + "node_modules/postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/postcss-minify-font-values/download/postcss-minify-font-values-1.0.5.tgz?cache=0&sync_timestamp=1599670482669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-minify-font-values%2Fdownload%2Fpostcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "dev": true, + "dependencies": { + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "node_modules/postcss-minify-font-values/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-minify-font-values/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-font-values/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/postcss-minify-gradients/download/postcss-minify-gradients-1.0.5.tgz?cache=0&sync_timestamp=1599670482761&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-minify-gradients%2Fdownload%2Fpostcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "dev": true, + "dependencies": { + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-minify-gradients/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-gradients/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npm.taobao.org/postcss-minify-params/download/postcss-minify-params-1.2.2.tgz?cache=0&sync_timestamp=1599670482880&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-minify-params%2Fdownload%2Fpostcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss-minify-params/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-params/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-params/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-params/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-minify-params/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-params/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/postcss-minify-selectors/download/postcss-minify-selectors-2.1.1.tgz?cache=0&sync_timestamp=1599670483082&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-minify-selectors%2Fdownload%2Fpostcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-minify-selectors/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-minify-selectors/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npm.taobao.org/postcss-modules-extract-imports/download/postcss-modules-extract-imports-1.2.1.tgz?cache=0&sync_timestamp=1600776084988&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-modules-extract-imports%2Fdownload%2Fpostcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha1-3IfjQUjsfqtfeR981YSYMzdbdBo=", + "dev": true, + "dependencies": { + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/postcss-modules-local-by-default/download/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/postcss-modules-scope/download/postcss-modules-scope-1.1.0.tgz?cache=0&sync_timestamp=1600778003060&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-modules-scope%2Fdownload%2Fpostcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/postcss-modules-values/download/postcss-modules-values-1.3.0.tgz?cache=0&sync_timestamp=1602187018990&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-modules-values%2Fdownload%2Fpostcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "dependencies": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/postcss-normalize-charset/download/postcss-normalize-charset-1.1.1.tgz?cache=0&sync_timestamp=1599670482967&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-charset%2Fdownload%2Fpostcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "dependencies": { + "postcss": "^5.0.5" + } + }, + "node_modules/postcss-normalize-charset/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-normalize-charset/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-normalize-positions/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "dependencies": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "dependencies": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-normalize-string/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "dependencies": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npm.taobao.org/postcss-normalize-url/download/postcss-normalize-url-3.0.8.tgz?cache=0&sync_timestamp=1599670483753&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-normalize-url%2Fdownload%2Fpostcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "dev": true, + "dependencies": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" + } + }, + "node_modules/postcss-normalize-url/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-normalize-url/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-url/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npm.taobao.org/postcss-ordered-values/download/postcss-ordered-values-2.2.3.tgz?cache=0&sync_timestamp=1599670483946&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-ordered-values%2Fdownload%2Fpostcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" + } + }, + "node_modules/postcss-ordered-values/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-ordered-values/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-ordered-values/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-pxtorem": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-pxtorem/-/postcss-pxtorem-5.1.1.tgz", + "integrity": "sha512-uvgIujL/pn0GbZ+rczESD2orHsbXrrCqi+q9wJO8PCk3ZGCoVVtu5hZTbtk+tbZHZP5UkTfCvqOrTZs9Ncqfsg==", + "dependencies": { + "postcss": "^7.0.27" + } + }, + "node_modules/postcss-pxtorem/node_modules/postcss": { + "version": "7.0.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", + "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-pxtorem/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npm.taobao.org/postcss-reduce-idents/download/postcss-reduce-idents-2.4.0.tgz?cache=0&sync_timestamp=1599672339373&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-reduce-idents%2Fdownload%2Fpostcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "node_modules/postcss-reduce-idents/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-idents/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-idents/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-reduce-idents/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-idents/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-reduce-idents/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-idents/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/postcss-reduce-initial/download/postcss-reduce-initial-1.0.1.tgz?cache=0&sync_timestamp=1599670484036&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-reduce-initial%2Fdownload%2Fpostcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "dev": true, + "dependencies": { + "postcss": "^5.0.4" + } + }, + "node_modules/postcss-reduce-initial/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-reduce-initial/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-initial/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/postcss-reduce-transforms/download/postcss-reduce-transforms-1.0.4.tgz?cache=0&sync_timestamp=1599670484213&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-reduce-transforms%2Fdownload%2Fpostcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "dev": true, + "dependencies": { + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npm.taobao.org/postcss-selector-parser/download/postcss-selector-parser-2.2.3.tgz?cache=0&sync_timestamp=1601045323543&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-selector-parser%2Fdownload%2Fpostcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "dev": true, + "dependencies": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "node_modules/postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npm.taobao.org/postcss-svgo/download/postcss-svgo-2.1.6.tgz?cache=0&sync_timestamp=1599670484324&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-svgo%2Fdownload%2Fpostcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "dev": true, + "dependencies": { + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" + } + }, + "node_modules/postcss-svgo/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-svgo/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-svgo/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/postcss-unique-selectors/download/postcss-unique-selectors-2.0.2.tgz?cache=0&sync_timestamp=1599670484407&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-unique-selectors%2Fdownload%2Fpostcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "dev": true, + "dependencies": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-unique-selectors/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-unique-selectors/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-url": { + "version": "7.3.2", + "resolved": "https://registry.npm.taobao.org/postcss-url/download/postcss-url-7.3.2.tgz", + "integrity": "sha1-X+onOAf7hLOMRhw8mp6KvSNfcSA=", + "dev": true, + "dependencies": { + "mime": "^1.4.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.0", + "postcss": "^6.0.1", + "xxhashjs": "^0.2.1" + } + }, + "node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npm.taobao.org/postcss-value-parser/download/postcss-value-parser-3.3.1.tgz", + "integrity": "sha1-n/giVH4okyE88cMO+lGsX9G6goE=", + "dev": true + }, + "node_modules/postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/postcss-zindex/download/postcss-zindex-2.2.0.tgz?cache=0&sync_timestamp=1599670556187&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-zindex%2Fdownload%2Fpostcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", + "dev": true, + "dependencies": { + "has": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "node_modules/postcss-zindex/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-zindex/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-zindex/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/postcss-zindex/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-zindex/node_modules/postcss": { + "version": "5.2.18", + "resolved": "https://registry.npm.taobao.org/postcss/download/postcss-5.2.18.tgz", + "integrity": "sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/postcss-zindex/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-zindex/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-3.2.3.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/prepend-http/download/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prettier": { + "version": "1.19.1", + "resolved": "https://registry.npm.taobao.org/prettier/download/prettier-1.19.1.tgz?cache=0&sync_timestamp=1600217249280&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fprettier%2Fdownload%2Fprettier-1.19.1.tgz", + "integrity": "sha1-99f1/4qc2HKnvkyhQglZVqYHl8s=", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/pretty-error/download/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "dependencies": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "node_modules/private": { + "version": "0.1.8", + "resolved": "https://registry.npm.taobao.org/private/download/private-0.1.8.tgz", + "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npm.taobao.org/process/download/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.1.tgz", + "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=", + "dev": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/promise-inflight/download/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, + "node_modules/proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npm.taobao.org/proxy-addr/download/proxy-addr-2.0.6.tgz", + "integrity": "sha1-/cIzZQVEfT8vLGOO0nLK9hS7sr8=", + "dev": true, + "dependencies": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/pseudomap/download/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npm.taobao.org/public-encrypt/download/public-encrypt-4.0.3.tgz", + "integrity": "sha1-T8ydd6B+SLp1J+fL4N4z0HATMeA=", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-4.11.9.tgz", + "integrity": "sha1-JtVWgpRY+dHoH8SJUkk9C6NQeCg=", + "dev": true + }, + "node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/pump/download/pump-2.0.1.tgz", + "integrity": "sha1-Ejma3W5M91Jtlzy8i1zi4pCLOQk=", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npm.taobao.org/pumpify/download/pumpify-1.5.1.tgz", + "integrity": "sha1-NlE74karJ1cLGjdKXOJ4v9dDcM4=", + "dev": true, + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npm.taobao.org/q/download/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.7.0", + "resolved": "https://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz", + "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/query-string": { + "version": "4.3.4", + "resolved": "https://registry.npm.taobao.org/query-string/download/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npm.taobao.org/querystring/download/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/querystring-es3/download/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/querystringify/download/querystringify-2.2.0.tgz", + "integrity": "sha1-M0WUG0FTy50ILY7uTNogFqmu9/Y=", + "dev": true + }, + "node_modules/quick-lru": { + "version": "6.1.1", + "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-6.1.1.tgz", + "integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==", + "engines": { + "node": ">=12" + } + }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/randombytes/download/randombytes-2.1.0.tgz", + "integrity": "sha1-32+ENy8CcNxlzfYpE0mrekc9Tyo=", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/randomfill/download/randomfill-1.0.4.tgz", + "integrity": "sha1-ySGW/IarQr6YPxvzF3giSTHWFFg=", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npm.taobao.org/range-parser/download/range-parser-1.2.1.tgz", + "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npm.taobao.org/raw-body/download/raw-body-2.4.0.tgz", + "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", + "dev": true, + "dependencies": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dependencies": { + "quickselect": "^2.0.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/read-cache/download/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/read-pkg/download/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "dependencies": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/read-pkg-up/download/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/path-type/download/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "dependencies": { + "pify": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.7.tgz", + "integrity": "sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-3.4.0.tgz", + "integrity": "sha1-n9zN+ekVWAVEkiGsZF6DA6tbmto=", + "dev": true, + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npm.taobao.org/rechoir/download/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/redent": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/redent/download/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "dependencies": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/reduce-css-calc/download/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dev": true, + "dependencies": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + } + }, + "node_modules/reduce-css-calc/node_modules/balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + }, + "node_modules/reduce-function-call": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/reduce-function-call/download/reduce-function-call-1.0.3.tgz", + "integrity": "sha1-YDUPf7JSwKZ+sQ/UaU0WkJlxMA8=", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.1", + "resolved": "https://registry.npm.taobao.org/regenerate/download/regenerate-1.4.1.tgz", + "integrity": "sha1-ytkq2Oa1kXc0hfvgWkhcr09Ffm8=", + "dev": true + }, + "node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz?cache=0&sync_timestamp=1595456117883&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.11.1.tgz", + "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk=" + }, + "node_modules/regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npm.taobao.org/regenerator-transform/download/regenerator-transform-0.10.1.tgz", + "integrity": "sha1-HkmWg3Ix2ot/PPQRTXG1aRoGgN0=", + "dev": true, + "dependencies": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/regex-not/download/regex-not-1.0.2.tgz", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/regexp.prototype.flags/download/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha1-erqJs8E6ZFCdq888qNn7ub31y3U=", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/regexpu-core/download/regexpu-core-2.0.0.tgz?cache=0&sync_timestamp=1600413529161&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregexpu-core%2Fdownload%2Fregexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "dependencies": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "node_modules/regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npm.taobao.org/regjsgen/download/regjsgen-0.2.0.tgz?cache=0&sync_timestamp=1590335923060&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fregjsgen%2Fdownload%2Fregjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npm.taobao.org/regjsparser/download/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npm.taobao.org/jsesc/download/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npm.taobao.org/relateurl/download/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/remove-trailing-separator/download/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "node_modules/renderkid": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + } + }, + "node_modules/renderkid/node_modules/css-select": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "node_modules/renderkid/node_modules/css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "node_modules/renderkid/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "node_modules/renderkid/node_modules/domhandler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "node_modules/renderkid/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/renderkid/node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + } + }, + "node_modules/repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/repeat-element/download/repeat-element-1.1.3.tgz", + "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npm.taobao.org/repeat-string/download/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/repeating/download/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/require-directory/download/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npm.taobao.org/require-from-string/download/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/require-main-filename/download/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/requires-port/download/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.17.0.tgz", + "integrity": "sha1-sllBtUloIxzC0bt2p5y38sC/hEQ=", + "dev": true, + "dependencies": { + "path-parse": "^1.0.6" + } + }, + "node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/resolve-cwd/download/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/resolve-from/download/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npm.taobao.org/resolve-url/download/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/restore-cursor/download/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npm.taobao.org/ret/download/ret-0.1.15.tgz", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "node_modules/rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "node_modules/right-align": { + "version": "0.1.3", + "resolved": "https://registry.npm.taobao.org/right-align/download/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "dependencies": { + "align-text": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-2.7.1.tgz", + "integrity": "sha1-NXl/E6f9rcVmFCwp1PB8ytSD4+w=", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/ripemd160/download/ripemd160-2.0.2.tgz", + "integrity": "sha1-ocGm9iR1FXe6XQeRTLyShQWFiQw=", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/run-queue/download/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "dependencies": { + "aproba": "^1.1.1" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", + "dev": true + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/safe-regex/download/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", + "dev": true + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npm.taobao.org/sax/download/sax-1.2.4.tgz?cache=0&sync_timestamp=1589682064084&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsax%2Fdownload%2Fsax-1.2.4.tgz", + "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", + "dev": true + }, + "node_modules/schema-utils": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.3.0.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.3.0.tgz", + "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", + "dev": true, + "dependencies": { + "ajv": "^5.0.0" + }, + "engines": { + "node": ">= 4.3 < 5.0.0 || >= 5.10" + } + }, + "node_modules/select": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/select/download/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/select-hose/download/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "node_modules/selfsigned": { + "version": "1.10.8", + "resolved": "https://registry.npm.taobao.org/selfsigned/download/selfsigned-1.10.8.tgz?cache=0&sync_timestamp=1600186082996&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fselfsigned%2Fdownload%2Fselfsigned-1.10.8.tgz", + "integrity": "sha1-DRcgi30Swz+OrIXEGDXyf8PYGjA=", + "dev": true, + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npm.taobao.org/semver/download/semver-5.7.1.tgz", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.17.1", + "resolved": "https://registry.npm.taobao.org/send/download/send-0.17.1.tgz", + "integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", + "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", + "dev": true + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npm.taobao.org/serve-index/download/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npm.taobao.org/http-errors/download/http-errors-1.6.3.tgz?cache=0&sync_timestamp=1593407611415&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-errors%2Fdownload%2Fhttp-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.0.tgz", + "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npm.taobao.org/serve-static/download/serve-static-1.14.1.tgz", + "integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/set-blocking/download/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/set-value/download/set-value-2.0.1.tgz", + "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npm.taobao.org/setimmediate/download/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.1.tgz", + "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npm.taobao.org/sha.js/download/sha.js-2.4.11.tgz", + "integrity": "sha1-N6XPC4HsvGlD3hCbopYNGyZYSuc=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/shebang-regex/download/shebang-regex-1.0.0.tgz?cache=0&sync_timestamp=1596697357985&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fshebang-regex%2Fdownload%2Fshebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmmirror.com/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npm.taobao.org/shellwords/download/shellwords-0.1.1.tgz", + "integrity": "sha1-1rkYHBpI05cyTISHHvvPxz/AZUs=", + "dev": true + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.3.tgz", + "integrity": "sha1-oUEMLt2PB3sItOJTyOrPyvBXRhw=", + "dev": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/slash/download/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npm.taobao.org/snapdragon/download/snapdragon-0.8.2.tgz", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/snapdragon-node/download/snapdragon-node-2.1.1.tgz", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/is-descriptor/download/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-6.0.3.tgz", + "integrity": "sha1-B8BQNKbDSfoG4k+jWqdttFgM5N0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/snapdragon-util/download/snapdragon-util-3.0.1.tgz", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npm.taobao.org/sockjs/download/sockjs-0.3.19.tgz", + "integrity": "sha1-2Xa76ACve9IK4IWY1YI5NQiZPA0=", + "dev": true, + "dependencies": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "node_modules/sockjs-client": { + "version": "1.1.5", + "resolved": "https://registry.npm.taobao.org/sockjs-client/download/sockjs-client-1.1.5.tgz?cache=0&sync_timestamp=1596409908572&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsockjs-client%2Fdownload%2Fsockjs-client-1.1.5.tgz", + "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=", + "dev": true, + "dependencies": { + "debug": "^2.6.6", + "eventsource": "0.1.6", + "faye-websocket": "~0.11.0", + "inherits": "^2.0.1", + "json3": "^3.3.2", + "url-parse": "^1.1.8" + } + }, + "node_modules/sockjs-client/node_modules/faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npm.taobao.org/faye-websocket/download/faye-websocket-0.11.3.tgz", + "integrity": "sha1-XA6aiWjokSwoZjn96XeosgnyUI4=", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/sort-asc": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/sort-asc/-/sort-asc-0.1.0.tgz", + "integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-desc": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/sort-desc/-/sort-desc-0.1.1.tgz", + "integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/sort-keys/download/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-object": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/sort-object/-/sort-object-0.3.2.tgz", + "integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==", + "dependencies": { + "sort-asc": "^0.1.0", + "sort-desc": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/source-list-map/download/source-list-map-2.0.1.tgz", + "integrity": "sha1-OZO9hzv8SEecyp6jpUeDXHwVSzQ=", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npm.taobao.org/source-map-resolve/download/source-map-resolve-0.5.3.tgz", + "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npm.taobao.org/source-map-support/download/source-map-support-0.4.18.tgz", + "integrity": "sha1-Aoam3ovkJkEzhZTpfM6nXwosWF8=", + "dev": true, + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npm.taobao.org/source-map-url/download/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/spdx-correct/download/spdx-correct-3.1.1.tgz?cache=0&sync_timestamp=1590161967473&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fspdx-correct%2Fdownload%2Fspdx-correct-3.1.1.tgz", + "integrity": "sha1-3s6BrJweZxPl99G28X1Gj6U9iak=", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/spdx-exceptions/download/spdx-exceptions-2.3.0.tgz", + "integrity": "sha1-PyjOGnegA3JoPq3kpDMYNSeiFj0=", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/spdx-expression-parse/download/spdx-expression-parse-3.0.1.tgz?cache=0&sync_timestamp=1589682217985&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fspdx-expression-parse%2Fdownload%2Fspdx-expression-parse-3.0.1.tgz", + "integrity": "sha1-z3D1BILu/cmOPOCmgz5KU87rpnk=", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.6", + "resolved": "https://registry.npm.taobao.org/spdx-license-ids/download/spdx-license-ids-3.0.6.tgz?cache=0&sync_timestamp=1600286627478&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fspdx-license-ids%2Fdownload%2Fspdx-license-ids-3.0.6.tgz", + "integrity": "sha1-yAdXODwoq/cpZ0SZjLwQaui4VM4=", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npm.taobao.org/spdy/download/spdy-4.0.2.tgz", + "integrity": "sha1-t09GYgOj7aRSwCSSuR+56EonZ3s=", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/spdy-transport/download/spdy-transport-3.0.0.tgz", + "integrity": "sha1-ANSGOmQArXXfkzYaFghgXl3NzzE=", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502873540&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy-transport/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz", + "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/spdy/node_modules/debug": { + "version": "4.2.0", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz?cache=0&sync_timestamp=1600502873540&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.2.0.tgz", + "integrity": "sha1-fxUPk5IOlMWPVXTC/QGjEQ7/5/E=", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/split-string/download/split-string-3.1.0.tgz", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", + "dev": true + }, + "node_modules/stackframe": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/stackframe/download/stackframe-1.2.0.tgz?cache=0&sync_timestamp=1590854186823&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstackframe%2Fdownload%2Fstackframe-1.2.0.tgz", + "integrity": "sha1-UkKUktY8YuuYmATBFVLj0i53kwM=", + "dev": true + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npm.taobao.org/static-extend/download/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/stream-browserify/download/stream-browserify-2.0.2.tgz", + "integrity": "sha1-h1IdOKRKp+6RzhzSpH3wy0ndZgs=", + "dev": true, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npm.taobao.org/stream-each/download/stream-each-1.2.3.tgz", + "integrity": "sha1-6+J6DDibBPvMIzZClS4Qcxr6m64=", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npm.taobao.org/stream-http/download/stream-http-2.8.3.tgz", + "integrity": "sha1-stJCRpKIpaJ+xP6JM6z2I95lFPw=", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/stream-shift/download/stream-shift-1.0.1.tgz", + "integrity": "sha1-1wiCgVWasneEJCebCHfaPDktWj0=", + "dev": true + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/strict-uri-encode/download/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-2.1.1.tgz", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-4.0.0.tgz?cache=0&sync_timestamp=1596697387823&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz?cache=0&sync_timestamp=1596697387823&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/strip-bom/download/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/strip-eof/download/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/strip-indent/download/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylehacks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.0.tgz", + "integrity": "sha1-ZLMjlRxKJOX8ey7AbBN78y0VXoo=", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "postcss": "^6.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/stylehacks/node_modules/browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/stylehacks/node_modules/postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-5.5.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/svgo": { + "version": "0.7.2", + "resolved": "https://registry.npm.taobao.org/svgo/download/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dev": true, + "dependencies": { + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/svgo/node_modules/js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/tapable": { + "version": "0.2.9", + "resolved": "https://registry.npm.taobao.org/tapable/download/tapable-0.2.9.tgz?cache=0&sync_timestamp=1600381257656&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftapable%2Fdownload%2Ftapable-0.2.9.tgz", + "integrity": "sha1-ry2LvJsE907hevK02QSPgHrNGKg=", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/throttle-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-1.1.0.tgz", + "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npm.taobao.org/through2/download/through2-2.0.5.tgz?cache=0&sync_timestamp=1593478647766&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fthrough2%2Fdownload%2Fthrough2-2.0.5.tgz", + "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/thunky/download/thunky-1.1.0.tgz", + "integrity": "sha1-Wrr3FKlAXbBQRzK7zNLO3Z75U30=", + "dev": true + }, + "node_modules/time-stamp": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/time-stamp/download/time-stamp-2.2.0.tgz", + "integrity": "sha1-kX4KZpBWiHkOx7u94EBGJZr4P1c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npm.taobao.org/timers-browserify/download/timers-browserify-2.0.11.tgz", + "integrity": "sha1-gAsfPu4nLlvFPuRloE0OgEwxIR8=", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/tiny-emitter/download/tiny-emitter-2.1.0.tgz", + "integrity": "sha1-HRpW7fxRxD6GPLtTgqcjMONVVCM=" + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npm.taobao.org/to-fast-properties/download/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npm.taobao.org/to-object-path/download/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npm.taobao.org/to-regex/download/to-regex-3.0.2.tgz", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-5.0.1.tgz", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", + "dev": true, + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/toidentifier/download/toidentifier-1.0.0.tgz", + "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort": { + "version": "1.0.7", + "resolved": "https://registry.npm.taobao.org/toposort/download/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + }, + "node_modules/trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/trim-newlines/download/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/trim-right/download/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/tryer/download/tryer-1.0.1.tgz", + "integrity": "sha1-8shUBoALmw90yfdGW4HqrSQSUvg=", + "dev": true + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npm.taobao.org/tty-browserify/download/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/type/download/type-1.2.0.tgz?cache=0&sync_timestamp=1598016600310&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftype%2Fdownload%2Ftype-1.2.0.tgz", + "integrity": "sha1-hI3XaY2vo+VKbEeedZxLw/GIR6A=", + "dev": true + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npm.taobao.org/type-is/download/type-is-1.6.18.tgz", + "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz?cache=0&sync_timestamp=1596697411295&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftypedarray%2Fdownload%2Ftypedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "node_modules/uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npm.taobao.org/uglify-js/download/uglify-js-3.4.10.tgz?cache=0&sync_timestamp=1601823880483&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuglify-js%2Fdownload%2Fuglify-js-3.4.10.tgz", + "integrity": "sha1-mtlWPY6zrN+404WX0q8dgV9qdV8=", + "dev": true, + "dependencies": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uglify-js/node_modules/commander": { + "version": "2.19.0", + "resolved": "https://registry.npm.taobao.org/commander/download/commander-2.19.0.tgz?cache=0&sync_timestamp=1598576136669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.19.0.tgz", + "integrity": "sha1-9hmKqE5bg8RgVLlN3tv+1e6f8So=", + "dev": true + }, + "node_modules/uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/uglify-to-browserify/download/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "node_modules/uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npm.taobao.org/uglifyjs-webpack-plugin/download/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha1-dfVIFghYFjoIZD4IbV/v4YpdZ94=", + "dev": true, + "dependencies": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "engines": { + "node": ">= 4.8 < 5.0.0 || >= 5.10" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/ajv": { + "version": "6.12.5", + "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", + "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/commander": { + "version": "2.13.0", + "resolved": "https://registry.npm.taobao.org/commander/download/commander-2.13.0.tgz?cache=0&sync_timestamp=1598576136669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.13.0.tgz", + "integrity": "sha1-aWS8pnaF33wfFDDFhPB9dZeIW5w=", + "dev": true + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", + "dev": true + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npm.taobao.org/schema-utils/download/schema-utils-0.4.7.tgz?cache=0&sync_timestamp=1601922251376&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fschema-utils%2Fdownload%2Fschema-utils-0.4.7.tgz", + "integrity": "sha1-unT1l9K+LqiAExdG7hfQoJPGgYc=", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/uglifyjs-webpack-plugin/node_modules/uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npm.taobao.org/uglify-es/download/uglify-es-3.3.9.tgz", + "integrity": "sha1-DBxPBwC+2NvBJM2zBNJZLKID5nc=", + "deprecated": "support for ECMAScript is superseded by `uglify-js` as of v3.13.0", + "dev": true, + "dependencies": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/union-value/download/union-value-1.0.1.tgz", + "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/uniq/download/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "node_modules/uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/uniqs/download/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/unique-filename/download/unique-filename-1.1.1.tgz", + "integrity": "sha1-HWl2k2mtoFgxA6HmrodoG1ZXMjA=", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npm.taobao.org/unique-slug/download/unique-slug-2.0.2.tgz", + "integrity": "sha1-uqvOkQg/xk6UWw861hPiZPfNTmw=", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/unset-value/download/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npm.taobao.org/has-value/download/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/isobject/download/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npm.taobao.org/has-values/download/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/upath/download/upath-1.2.0.tgz", + "integrity": "sha1-j2bbzVWog6za5ECK+LA1pQRMGJQ=", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npm.taobao.org/upper-case/download/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npm.taobao.org/uri-js/download/uri-js-4.4.0.tgz?cache=0&sync_timestamp=1598814377097&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Furi-js%2Fdownload%2Furi-js-4.4.0.tgz", + "integrity": "sha1-qnFCYd55PoqCNHp7zJznTobyhgI=", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npm.taobao.org/urix/download/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npm.taobao.org/url/download/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-loader": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.9.tgz", + "integrity": "sha512-B7QYFyvv+fOBqBVeefsxv6koWWtjmHaMFT6KZWti4KRw8YUD/hOU+3AECvXuzyVawIBx3z7zQRejXCDSO5kk1Q==", + "dev": true, + "dependencies": { + "loader-utils": "^1.0.2", + "mime": "1.3.x" + }, + "peerDependencies": { + "file-loader": "*" + } + }, + "node_modules/url-loader/node_modules/mime": { + "version": "1.3.6", + "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.3.6.tgz", + "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=", + "dev": true, + "bin": { + "mime": "cli.js" + } + }, + "node_modules/url-parse": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz", + "integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npm.taobao.org/use/download/use-3.1.1.tgz", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npm.taobao.org/util/download/util-0.11.1.tgz?cache=0&sync_timestamp=1596697422093&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Futil%2Fdownload%2Futil-0.11.1.tgz", + "integrity": "sha1-MjZzNyDsZLsn9uJvQhqqLhtYjWE=", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npm.taobao.org/utila/download/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "node_modules/utils-lite": { + "version": "0.1.10", + "resolved": "https://registry.npmmirror.com/utils-lite/-/utils-lite-0.1.10.tgz", + "integrity": "sha512-jlHvdtI8MyWURF/3u+ufIjf1Cs5WjN6WZl9qO8dEkZsVjaI7X5YMUhaCFzkvB69ljt6fo4Dd7V/Oj2NJOFDFOQ==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v-charts": { + "version": "1.19.0", + "resolved": "https://registry.npmmirror.com/v-charts/-/v-charts-1.19.0.tgz", + "integrity": "sha512-vm2HBUmxAsXK0ivwce9LytcpqrItDA5JSPLYVxZXtiuoyhcn80XX1/3dPJd/1GqG1OYv3jfBo1s9ra4q8GowqA==", + "dependencies": { + "echarts-amap": "1.0.0-rc.6", + "echarts-liquidfill": "^2.0.2", + "echarts-wordcloud": "^1.1.3", + "numerify": "1.2.9", + "utils-lite": "0.1.10" + }, + "peerDependencies": { + "echarts": ">3.0.0", + "vue": ">2.0.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npm.taobao.org/validate-npm-package-license/download/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vendors": { + "version": "1.0.4", + "resolved": "https://registry.npm.taobao.org/vendors/download/vendors-1.0.4.tgz", + "integrity": "sha1-4rgApT56Kbk1BsPPQRANFsTErY4=", + "dev": true + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/vm-browserify/download/vm-browserify-1.1.2.tgz", + "integrity": "sha1-eGQcSIuObKkadfUR56OzKobl3aA=", + "dev": true + }, + "node_modules/vue": { + "version": "2.6.12", + "resolved": "https://registry.npm.taobao.org/vue/download/vue-2.6.12.tgz?cache=0&sync_timestamp=1600441238751&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue%2Fdownload%2Fvue-2.6.12.tgz", + "integrity": "sha1-9evU+mvShpQD4pqJau1JBEVskSM=" + }, + "node_modules/vue-clipboard2": { + "version": "0.3.1", + "resolved": "https://registry.npm.taobao.org/vue-clipboard2/download/vue-clipboard2-0.3.1.tgz", + "integrity": "sha1-blUft704SImyiw2jsSKJ7WvKSJQ=", + "dependencies": { + "clipboard": "^2.0.0" + } + }, + "node_modules/vue-clipboards": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/vue-clipboards/-/vue-clipboards-1.3.0.tgz", + "integrity": "sha512-VMDYHpLQH0EUmqfk9b5XMrkvSu/HjNsLW2EBR4OS6JZHcv/PxmWYdoTBPVlp5eYrhWy07La8nWpRwAh09Mgufw==", + "dependencies": { + "clipboard": "^1.7.1" + } + }, + "node_modules/vue-clipboards/node_modules/clipboard": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz", + "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=", + "dependencies": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "node_modules/vue-contextmenujs": { + "version": "1.3.13", + "resolved": "https://registry.npmmirror.com/vue-contextmenujs/download/vue-contextmenujs-1.3.13.tgz", + "integrity": "sha1-O9rgI8e9QgleeNpCWAACUNUKuO8=" + }, + "node_modules/vue-cookies": { + "version": "1.7.4", + "resolved": "https://registry.npm.taobao.org/vue-cookies/download/vue-cookies-1.7.4.tgz?cache=0&sync_timestamp=1598941352058&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-cookies%2Fdownload%2Fvue-cookies-1.7.4.tgz", + "integrity": "sha1-0kHQoEMdoHlYN2UdELTXPnyNPo0=" + }, + "node_modules/vue-giant-tree": { + "version": "0.1.5", + "resolved": "https://registry.npmmirror.com/vue-giant-tree/-/vue-giant-tree-0.1.5.tgz", + "integrity": "sha512-P3KEHSZU2NkpWl6frss+sJLO0DLrtarMNLeTV/IGU2/w50rgrlKbKNr/ckK6BBVdWXAJYlYf6HUTNkKvGq5hlg==", + "dependencies": { + "@ztree/ztree_v3": "^3.5.44", + "jquery": "^3.5.1" + } + }, + "node_modules/vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npm.taobao.org/vue-hot-reload-api/download/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha1-UylVzB6yCKPZkLOp+acFdGV+CPI=", + "dev": true + }, + "node_modules/vue-loader": { + "version": "13.7.3", + "resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-13.7.3.tgz?cache=0&sync_timestamp=1600850410121&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-13.7.3.tgz", + "integrity": "sha1-4HRA94IwpjnQCtpNp7ltDp1iA38=", + "dev": true, + "dependencies": { + "consolidate": "^0.14.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "lru-cache": "^4.1.1", + "postcss": "^6.0.8", + "postcss-load-config": "^1.1.0", + "postcss-selector-parser": "^2.0.0", + "prettier": "^1.7.0", + "resolve": "^1.4.0", + "source-map": "^0.6.1", + "vue-hot-reload-api": "^2.2.0", + "vue-style-loader": "^3.0.0", + "vue-template-es2015-compiler": "^1.6.0" + }, + "peerDependencies": { + "css-loader": "*", + "vue-template-compiler": "^2.0.0" + } + }, + "node_modules/vue-loader/node_modules/cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npm.taobao.org/cosmiconfig/download/cosmiconfig-2.2.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcosmiconfig%2Fdownload%2Fcosmiconfig-2.2.2.tgz", + "integrity": "sha1-YXPOvVb6wELB9DkO33r2wHx8uJI=", + "dev": true, + "dependencies": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/vue-loader/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz?cache=0&sync_timestamp=1598130878813&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vue-loader/node_modules/postcss-load-config": { + "version": "1.2.0", + "resolved": "https://registry.npm.taobao.org/postcss-load-config/download/postcss-load-config-1.2.0.tgz?cache=0&sync_timestamp=1601607668112&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpostcss-load-config%2Fdownload%2Fpostcss-load-config-1.2.0.tgz", + "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", + "dev": true, + "dependencies": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0", + "postcss-load-options": "^1.2.0", + "postcss-load-plugins": "^2.3.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/vue-router": { + "version": "3.4.6", + "resolved": "https://registry.npm.taobao.org/vue-router/download/vue-router-3.4.6.tgz?cache=0&sync_timestamp=1602076636169&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-router%2Fdownload%2Fvue-router-3.4.6.tgz", + "integrity": "sha1-972iyaQ9OYN2IcmgK6d4n12qJLI=" + }, + "node_modules/vue-style-loader": { + "version": "3.1.2", + "resolved": "https://registry.npm.taobao.org/vue-style-loader/download/vue-style-loader-3.1.2.tgz", + "integrity": "sha1-a2atNJmPyVIMLx5NX6QJFkHBWXo=", + "dev": true, + "dependencies": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.6.12", + "resolved": "https://registry.npm.taobao.org/vue-template-compiler/download/vue-template-compiler-2.6.12.tgz?cache=0&sync_timestamp=1597927391993&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-template-compiler%2Fdownload%2Fvue-template-compiler-2.6.12.tgz", + "integrity": "sha1-lH7XGWdEyKUoXr4SM/6WBDf8xX4=", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "node_modules/vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npm.taobao.org/vue-template-es2015-compiler/download/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha1-HuO8mhbsv1EYvjNLsV+cRvgvWCU=", + "dev": true + }, + "node_modules/vue-ztree-2.0": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/vue-ztree-2.0/-/vue-ztree-2.0-1.0.4.tgz", + "integrity": "sha512-d7KZsquEYpM0jD/k1uwOMFCd08L6++7zwRESaL2sF43OtRFCump8BxcLpjusBIHpFadPvOSMMnK5P41y+ZiTlA==" + }, + "node_modules/watchpack": { + "version": "1.7.4", + "resolved": "https://registry.npm.taobao.org/watchpack/download/watchpack-1.7.4.tgz?cache=0&sync_timestamp=1600385388649&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwatchpack%2Fdownload%2Fwatchpack-1.7.4.tgz", + "integrity": "sha1-bp2lOzyAuy1lCBiPWyAEEIZs0ws=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.0" + } + }, + "node_modules/watchpack-chokidar2": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/watchpack-chokidar2/download/watchpack-chokidar2-2.0.0.tgz", + "integrity": "sha1-mUihhmy71suCTeoTp+1pH2yN3/A=", + "dev": true, + "optional": true, + "dependencies": { + "chokidar": "^2.1.8" + }, + "engines": { + "node": "<8.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-2.0.0.tgz", + "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", + "dev": true, + "optional": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/normalize-path/download/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-1.13.1.tgz?cache=0&sync_timestamp=1593261283449&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbinary-extensions%2Fdownload%2Fbinary-extensions-1.13.1.tgz", + "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npm.taobao.org/braces/download/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "dev": true, + "optional": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npm.taobao.org/chokidar/download/chokidar-2.1.8.tgz?cache=0&sync_timestamp=1597763177396&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchokidar%2Fdownload%2Fchokidar-2.1.8.tgz", + "integrity": "sha1-gEs6e2qZNYw8XGHnHYco8EHP+Rc=", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "optional": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/watchpack-chokidar2/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-1.2.13.tgz", + "integrity": "sha1-8yXLBFVZJCi88Rs4M3DvcOO/zDg=", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/is-binary-path/download/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-2.2.1.tgz", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "dev": true, + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/watchpack-chokidar2/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "optional": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npm.taobao.org/wbuf/download/wbuf-1.7.3.tgz", + "integrity": "sha1-wdjRSTFtPqhShIiVy2oL/oh7h98=", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" + }, + "node_modules/webfont-matcher": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/webfont-matcher/-/webfont-matcher-1.1.0.tgz", + "integrity": "sha512-ov8lMvF9wi4PD7fK2Axn9PQEpO9cYI0fIoGqErwd+wi8xacFFDmX114D5Q2Lw0Wlgmb+Qw/dKI2KTtimrJf85g==" + }, + "node_modules/webpack": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", + "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", + "dev": true, + "dependencies": { + "acorn": "^5.0.0", + "acorn-dynamic-import": "^2.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "async": "^2.1.2", + "enhanced-resolve": "^3.4.0", + "escope": "^3.6.0", + "interpret": "^1.0.0", + "json-loader": "^0.5.4", + "json5": "^0.5.1", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "mkdirp": "~0.5.0", + "node-libs-browser": "^2.0.0", + "source-map": "^0.5.3", + "supports-color": "^4.2.1", + "tapable": "^0.2.7", + "uglifyjs-webpack-plugin": "^0.4.6", + "watchpack": "^1.4.0", + "webpack-sources": "^1.0.1", + "yargs": "^8.0.2" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz", + "integrity": "sha512-rwxyfecTAxoarCC9VlHlIpfQCmmJ/qWD5bpbjkof+7HrNhTNZIwZITxN6CdlYL2axGmwNUQ+tFgcSOiNXMf/sQ==", + "dev": true, + "dependencies": { + "acorn": "^5.3.0", + "bfj-node4": "^5.2.0", + "chalk": "^2.3.0", + "commander": "^2.13.0", + "ejs": "^2.5.7", + "express": "^4.16.2", + "filesize": "^3.5.11", + "gzip-size": "^4.1.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "opener": "^1.4.3", + "ws": "^4.0.0" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.npm.taobao.org/webpack-dev-middleware/download/webpack-dev-middleware-1.12.2.tgz?cache=0&sync_timestamp=1594744509096&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-dev-middleware%2Fdownload%2Fwebpack-dev-middleware-1.12.2.tgz", + "integrity": "sha1-+PwRIM47T8VoDO7LQ9d3lmshEF4=", + "dev": true, + "dependencies": { + "memory-fs": "~0.4.1", + "mime": "^1.5.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "time-stamp": "^2.0.0" + }, + "engines": { + "node": ">=0.6" + }, + "peerDependencies": { + "webpack": "^1.0.0 || ^2.0.0 || ^3.0.0" + } + }, + "node_modules/webpack-dev-server": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz", + "integrity": "sha512-7TdOKKt7G3sWEhPKV0zP+nD0c4V9YKUJ3wDdBwQsZNo58oZIRoVIu66pg7PYkBW8A74msP9C2kLwmxGHndz/pw==", + "dev": true, + "dependencies": { + "ansi-html": "0.0.7", + "array-includes": "^3.0.3", + "bonjour": "^3.5.0", + "chokidar": "^2.1.2", + "compression": "^1.7.3", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "^0.19.1", + "import-local": "^1.0.0", + "internal-ip": "1.2.0", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "selfsigned": "^1.9.1", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.1.5", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", + "webpack-dev-middleware": "1.12.2", + "yargs": "6.6.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">=4.7" + }, + "peerDependencies": { + "webpack": "^2.2.0 || ^3.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/anymatch/download/anymatch-2.0.0.tgz", + "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/normalize-path/download/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npm.taobao.org/binary-extensions/download/binary-extensions-1.13.1.tgz?cache=0&sync_timestamp=1593261283449&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbinary-extensions%2Fdownload%2Fbinary-extensions-1.13.1.tgz", + "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npm.taobao.org/braces/download/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npm.taobao.org/chokidar/download/chokidar-2.1.8.tgz?cache=0&sync_timestamp=1597763177396&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchokidar%2Fdownload%2Fchokidar-2.1.8.tgz", + "integrity": "sha1-gEs6e2qZNYw8XGHnHYco8EHP+Rc=", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npm.taobao.org/cliui/download/cliui-3.2.0.tgz?cache=0&sync_timestamp=1597606145227&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz?cache=0&sync_timestamp=1600502873540&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.2.6.tgz", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/webpack-dev-server/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npm.taobao.org/find-up/download/find-up-1.1.2.tgz?cache=0&sync_timestamp=1597756298124&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffind-up%2Fdownload%2Ffind-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npm.taobao.org/fsevents/download/fsevents-1.2.13.tgz", + "integrity": "sha1-8yXLBFVZJCi88Rs4M3DvcOO/zDg=", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/is-binary-path/download/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/load-json-file/download/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npm.taobao.org/os-locale/download/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-2.2.0.tgz?cache=0&sync_timestamp=1598130878813&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/path-exists/download/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/path-type/download/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npm.taobao.org/read-pkg/download/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npm.taobao.org/read-pkg-up/download/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npm.taobao.org/readdirp/download/readdirp-2.2.1.tgz", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/strip-bom/download/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/which-module": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/which-module/download/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/yargs": { + "version": "6.6.0", + "resolved": "https://registry.npm.taobao.org/yargs/download/yargs-6.6.0.tgz?cache=0&sync_timestamp=1600660100032&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs%2Fdownload%2Fyargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^4.2.0" + } + }, + "node_modules/webpack-dev-server/node_modules/yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npm.taobao.org/yargs-parser/download/yargs-parser-4.2.1.tgz?cache=0&sync_timestamp=1601576779920&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs-parser%2Fdownload%2Fyargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0" + } + }, + "node_modules/webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npm.taobao.org/webpack-merge/download/webpack-merge-4.2.2.tgz?cache=0&sync_timestamp=1602063025787&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebpack-merge%2Fdownload%2Fwebpack-merge-4.2.2.tgz", + "integrity": "sha1-onxS6ng9E5iv0gh/VH17nS9DY00=", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npm.taobao.org/webpack-sources/download/webpack-sources-1.4.3.tgz", + "integrity": "sha1-7t2OwLko+/HL/plOItLYkPMwqTM=", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.5", + "resolved": "https://registry.npm.taobao.org/ajv/download/ajv-6.12.5.tgz?cache=0&sync_timestamp=1600886864349&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.5.tgz", + "integrity": "sha1-GbDouuj0duW6ZmMAOHd1+xoApNo=", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/webpack/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", + "dev": true + }, + "node_modules/webpack/node_modules/has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599334207614&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "node_modules/webpack/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-4.5.0.tgz?cache=0&sync_timestamp=1598611719015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "dependencies": { + "has-flag": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npm.taobao.org/uglify-js/download/uglify-js-2.8.29.tgz?cache=0&sync_timestamp=1601823880483&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuglify-js%2Fdownload%2Fuglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "dependencies": { + "source-map": "~0.5.1", + "yargs": "~3.10.0" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + }, + "optionalDependencies": { + "uglify-to-browserify": "~1.0.0" + } + }, + "node_modules/webpack/node_modules/uglify-js/node_modules/yargs": { + "version": "3.10.0", + "resolved": "https://registry.npm.taobao.org/yargs/download/yargs-3.10.0.tgz?cache=0&sync_timestamp=1600660100032&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs%2Fdownload%2Fyargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "dependencies": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + }, + "node_modules/webpack/node_modules/uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npm.taobao.org/uglifyjs-webpack-plugin/download/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + }, + "peerDependencies": { + "webpack": "^1.9 || ^2 || ^2.1.0-beta || ^2.2.0-rc || ^3.0.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npm.taobao.org/websocket-driver/download/websocket-driver-0.7.4.tgz?cache=0&sync_timestamp=1591289007652&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwebsocket-driver%2Fdownload%2Fwebsocket-driver-0.7.4.tgz", + "integrity": "sha1-ia1Slbv2S0gKvLox5JU6ynBvV2A=", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npm.taobao.org/websocket-extensions/download/websocket-extensions-0.1.4.tgz", + "integrity": "sha1-f4RzvIOd/YdgituV1+sHUhFXikI=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npm.taobao.org/whet.extend/download/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", + "dev": true, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npm.taobao.org/which/download/which-1.3.1.tgz", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npm.taobao.org/which-module/download/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/window-size": { + "version": "0.1.0", + "resolved": "https://registry.npm.taobao.org/window-size/download/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npm.taobao.org/worker-farm/download/worker-farm-1.7.0.tgz", + "integrity": "sha1-JqlMU5G7ypJhUgAvabhKS/dy5ag=", + "dev": true, + "dependencies": { + "errno": "~0.1.7" + } + }, + "node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/ws": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/ws/download/ws-4.1.0.tgz?cache=0&sync_timestamp=1593925601875&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fws%2Fdownload%2Fws-4.1.0.tgz", + "integrity": "sha1-qXm119TaaL9U7+BAiWfDJIaacok=", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0" + } + }, + "node_modules/xml-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/xml-utils/-/xml-utils-1.0.2.tgz", + "integrity": "sha512-rEn0FvKi+YGjv9omf22oAf+0d6Ly/sgJ/CUufU/nOzS7SRLmgwSujrewc03KojXxt+aPaTRpm593TgehtUBMSQ==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz?cache=0&sync_timestamp=1596697437792&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fxtend%2Fdownload%2Fxtend-4.0.2.tgz", + "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/xxhashjs": { + "version": "0.2.2", + "resolved": "https://registry.npm.taobao.org/xxhashjs/download/xxhashjs-0.2.2.tgz", + "integrity": "sha1-imJRVnYhocRqWuIE2gJJx/jKqdg=", + "dev": true, + "dependencies": { + "cuint": "^0.2.2" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "node_modules/yargs": { + "version": "8.0.2", + "resolved": "https://registry.npm.taobao.org/yargs/download/yargs-8.0.2.tgz?cache=0&sync_timestamp=1600660100032&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs%2Fdownload%2Fyargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "dev": true, + "dependencies": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + } + }, + "node_modules/yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npm.taobao.org/yargs-parser/download/yargs-parser-7.0.0.tgz?cache=0&sync_timestamp=1601576779920&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs-parser%2Fdownload%2Fyargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "dependencies": { + "camelcase": "^4.1.0" + } + }, + "node_modules/yargs-parser/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npm.taobao.org/camelcase/download/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/yargs/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npm.taobao.org/cliui/download/cliui-3.2.0.tgz?cache=0&sync_timestamp=1597606145227&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcliui%2Fdownload%2Fcliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/yargs/node_modules/cliui/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yargs/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/zrender": { + "version": "4.3.2", + "resolved": "https://registry.npm.taobao.org/zrender/download/zrender-4.3.2.tgz", + "integrity": "sha1-7HQy+UFcgsc1hLa3uMR+GwFiCcY=" + } + }, "dependencies": { + "@liveqing/liveplayer": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/@liveqing/liveplayer/-/liveplayer-2.7.0.tgz", + "integrity": "sha512-SWveQRqhhfJzkcpmHZxL6eLn+xLQuub888/JiBtUDHgt1eVwYYsorDiGcAKciNcyD70PuMfQ3+QrLoLbWE2vWA==" + }, + "@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==" + }, + "@mapbox/mapbox-gl-style-spec": { + "version": "13.23.1", + "resolved": "https://registry.npmmirror.com/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.23.1.tgz", + "integrity": "sha512-C6wh8A/5EdsgzhL6y6yl464VCQNIxK0yjrpnvCvchcFe3sNK2RbBw/J9u3m+p8Y6S6MsGuSMt3AkGAXOKMYweQ==", + "requires": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/unitbezier": "^0.0.0", + "csscolorparser": "~1.0.2", + "json-stringify-pretty-compact": "^2.0.0", + "minimist": "^1.2.5", + "rw": "^1.3.3", + "sort-object": "^0.3.2" + } + }, + "@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmmirror.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==" + }, + "@petamoriken/float16": { + "version": "3.6.3", + "resolved": "https://registry.npmmirror.com/@petamoriken/float16/-/float16-3.6.3.tgz", + "integrity": "sha512-Yx6Z93kmz3JVPYoPPRFJXnt2/G4kfaxRROcZVVHsE4zOClJXvkOVidv/JfvP6hWn16lykbKYKVzUsId6mqXdGg==" + }, "@types/q": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", "dev": true }, + "@ztree/ztree_v3": { + "version": "3.5.48", + "resolved": "https://registry.npmmirror.com/@ztree/ztree_v3/-/ztree_v3-3.5.48.tgz", + "integrity": "sha512-4dSA1g26T3j/O3I89+r/Palg+a+xwMGRS1etZoggnCGBPoOrwW8VGA3zitJCK/Yd7eEMX+LfKTRJjEGiWpoN3w==", + "requires": { + "jquery": ">=1.4.4" + } + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz", @@ -59,7 +14566,8 @@ "version": "3.5.2", "resolved": "https://registry.npm.taobao.org/ajv-keywords/download/ajv-keywords-3.5.2.tgz", "integrity": "sha1-MfKdpatuANHC0yms97WSlhTVAU0=", - "dev": true + "dev": true, + "requires": {} }, "align-text": { "version": "0.1.4", @@ -119,6 +14627,7 @@ "version": "1.0.10", "resolved": "https://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz", "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -261,8 +14770,8 @@ }, "async-validator": { "version": "1.8.5", - "resolved": "https://registry.nlark.com/async-validator/download/async-validator-1.8.5.tgz", - "integrity": "sha1-3D4I7B/Q3dtn5ghC8CwM0c7G1/A=", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.8.5.tgz", + "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==", "requires": { "babel-runtime": "6.x" } @@ -288,28 +14797,17 @@ } }, "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", "requires": { - "follow-redirects": "1.5.10" + "follow-redirects": "^1.14.4" }, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz?cache=0&sync_timestamp=1600502873540&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "requires": { - "ms": "2.0.0" - } - }, "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.5.10.tgz?cache=0&sync_timestamp=1597057997789&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.5.10.tgz", - "integrity": "sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=", - "requires": { - "debug": "=3.1.0" - } + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz", + "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==" } } }, @@ -1263,34 +15761,6 @@ "integrity": "sha1-nyKcFb4nJFT/qXOs4NvueaGww28=", "dev": true }, - "bmaplib.curveline": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bmaplib.curveline/-/bmaplib.curveline-1.0.0.tgz", - "integrity": "sha512-9wcFMVhiYxNPqpvsLDAADn3qDhNzXp2mA6VyHSHg2XOAgSooC7ZiujdFhy0sp+0QYjTfJ/MjmLuNoUg2HHxH4Q==" - }, - "bmaplib.heatmap": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/bmaplib.heatmap/-/bmaplib.heatmap-1.0.4.tgz", - "integrity": "sha512-rmhqUARBpUSJ9jXzUI2j7dIOqnc38bqubkx/8a349U2qtw/ulLUwyzRD535OrA8G7w5cz4aPKm6/rNvUAarg/Q==" - }, - "bmaplib.lushu": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/bmaplib.lushu/-/bmaplib.lushu-1.0.7.tgz", - "integrity": "sha512-LVvgpESPii6xGxyjnQjq8u+ic4NjvhdCPV/RiSS/PGTUdZKeTDS7prSpleJLZH3ES0+oc0gYn8bw0LtPYUSz2w==" - }, - "bmaplib.markerclusterer": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/bmaplib.markerclusterer/-/bmaplib.markerclusterer-1.0.13.tgz", - "integrity": "sha512-VrLyWSiuDEVNi0yUfwOhFQ6z1oEEHS4w36GNu3iASu6p52QIx9uAXMUkuSCHReNR0bj2Cp9SA1dSx5RpojXajQ==", - "requires": { - "bmaplib.texticonoverlay": "^1.0.2" - } - }, - "bmaplib.texticonoverlay": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bmaplib.texticonoverlay/-/bmaplib.texticonoverlay-1.0.2.tgz", - "integrity": "sha512-4ZTWr4ZP3B6qEWput5Tut16CfZgII38YwM3bpyb4gFTQyORlKYryFp9WHWrwZZaHlOyYDAXG9SX0hka43jTADg==" - }, "bn.js": { "version": "5.1.3", "resolved": "https://registry.npm.taobao.org/bn.js/download/bn.js-5.1.3.tgz", @@ -2323,18 +16793,6 @@ } } }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npm.taobao.org/css-select/download/css-select-1.2.0.tgz?cache=0&sync_timestamp=1601657992127&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-select%2Fdownload%2Fcss-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, "css-select-base-adapter": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", @@ -2361,11 +16819,10 @@ "source-map": "^0.6.1" } }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npm.taobao.org/css-what/download/css-what-2.1.3.tgz", - "integrity": "sha1-ptdgRXM2X+dGhsPzEcVlE9iChfI=", - "dev": true + "csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" }, "cssesc": { "version": "3.0.0", @@ -3120,8 +17577,8 @@ }, "deepmerge": { "version": "1.5.2", - "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-1.5.2.tgz?cache=0&sync_timestamp=1572279556265&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdeepmerge%2Fdownload%2Fdeepmerge-1.5.2.tgz", - "integrity": "sha1-EEmdhohEza1P7ghC34x/bwyVp1M=" + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==" }, "define-properties": { "version": "1.1.3", @@ -3356,25 +17813,6 @@ "integrity": "sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=", "dev": true }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npm.taobao.org/domhandler/download/domhandler-2.4.2.tgz", - "integrity": "sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npm.taobao.org/domutils/download/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, "dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -3404,12 +17842,28 @@ }, "echarts": { "version": "4.9.0", - "resolved": "https://registry.nlark.com/echarts/download/echarts-4.9.0.tgz?cache=0&sync_timestamp=1619495447964&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fecharts%2Fdownload%2Fecharts-4.9.0.tgz", - "integrity": "sha1-qbm6oD8Doqcx5jQMVb77V6nhNH0=", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-4.9.0.tgz", + "integrity": "sha512-+ugizgtJ+KmsJyyDPxaw2Br5FqzuBnyOWwcxPKO6y0gc5caYcfnEUIlNStx02necw8jmKmTafmpHhGo4XDtEIA==", "requires": { "zrender": "4.3.2" } }, + "echarts-amap": { + "version": "1.0.0-rc.6", + "resolved": "https://registry.npmmirror.com/echarts-amap/-/echarts-amap-1.0.0-rc.6.tgz", + "integrity": "sha512-cYJCKoQdnkZXrGweYrveU1HruZd1c0KmsF1U8o3FtsvgR2jVL5ZUpGFjMmFtpolHOUFqxizk+s+QBLkYuOWL6Q==" + }, + "echarts-liquidfill": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/echarts-liquidfill/-/echarts-liquidfill-2.0.6.tgz", + "integrity": "sha512-p+AH0O9/BtwXMQQyhjJbMZo+GwRAgWG/DCyK5r27PQzpS0UWrgXu57MyEFc0A8Ub3sRuqEu08BuxwHICBkSWSQ==", + "requires": {} + }, + "echarts-wordcloud": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/echarts-wordcloud/-/echarts-wordcloud-1.1.3.tgz", + "integrity": "sha512-Et8D5xEAoYkidmHun+hEH+2lF9dhCt6D0JJ390vlr2r/1zwhhZAbcL01CEvG93QcMcJpSvSPK8vRiGkTbMHRxg==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz", @@ -3429,9 +17883,9 @@ "dev": true }, "element-ui": { - "version": "2.15.1", - "resolved": "https://registry.npm.taobao.org/element-ui/download/element-ui-2.15.1.tgz?cache=0&sync_timestamp=1614082623756&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felement-ui%2Fdownload%2Felement-ui-2.15.1.tgz", - "integrity": "sha1-raAKpuMsAndKLndWPdhGaPgTzf8=", + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.15.6.tgz", + "integrity": "sha512-rcYXEKd/j2G0AgficAOk1Zd1AsnHRkhmrK4yLHmNOiimU2JfsywgfKUjMoFuT6pQx0luhovj8lFjpE4Fnt58Iw==", "requires": { "async-validator": "~1.8.1", "babel-helper-vue-jsx-merge-props": "^2.0.0", @@ -4232,6 +18686,27 @@ "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", "dev": true }, + "geotiff": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/geotiff/-/geotiff-2.0.5.tgz", + "integrity": "sha512-U5kVYm118YAmw2swiLu8rhfrYnDKOFI7VaMjuQwcq6Intuuid9Pyb4jjxYUxxkq8kOu2r7Am0Rmb52PObGp4pQ==", + "requires": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.0", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2" + }, + "dependencies": { + "pako": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==" + } + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npm.taobao.org/get-caller-file/download/get-caller-file-1.0.3.tgz", @@ -4282,9 +18757,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-5.1.1.tgz", - "integrity": "sha1-tsHvQXxOVmPqSY8cRa+saRa7wik=", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "optional": true, "requires": { @@ -4610,39 +19085,6 @@ } } }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.10.1.tgz?cache=0&sync_timestamp=1601762324362&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtmlparser2%2Fdownload%2Fhtmlparser2-3.10.1.tgz", - "integrity": "sha1-vWedw/WYl7ajS7EHSchVu1OpOS8=", - "dev": true, - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npm.taobao.org/entities/download/entities-1.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-1.1.2.tgz", - "integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=", - "dev": true - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz", - "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npm.taobao.org/http-deceiver/download/http-deceiver-1.2.7.tgz", @@ -4732,8 +19174,7 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npm.taobao.org/ieee754/download/ieee754-1.1.13.tgz", - "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=", - "dev": true + "integrity": "sha1-7BaFWOlaoYH9h9N/VcMrvLZwi4Q=" }, "iferr": { "version": "0.1.5", @@ -5161,6 +19602,11 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, "js-base64": { "version": "2.6.4", "resolved": "https://registry.npm.taobao.org/js-base64/download/js-base64-2.6.4.tgz?cache=0&sync_timestamp=1599897619557&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-base64%2Fdownload%2Fjs-base64-2.6.4.tgz", @@ -5175,7 +19621,7 @@ }, "js-yaml": { "version": "3.7.0", - "resolved": "https://registry.npm.taobao.org/js-yaml/download/js-yaml-3.7.0.tgz?cache=0&sync_timestamp=1590172281856&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-yaml%2Fdownload%2Fjs-yaml-3.7.0.tgz", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", "dev": true, "requires": { @@ -5207,6 +19653,11 @@ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, + "json-stringify-pretty-compact": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-2.0.0.tgz", + "integrity": "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ==" + }, "json3": { "version": "3.3.3", "resolved": "https://registry.npm.taobao.org/json3/download/json3-3.3.3.tgz", @@ -5259,13 +19710,10 @@ "invert-kv": "^1.0.0" } }, - "linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", - "requires": { - "uc.micro": "^1.0.1" - } + "lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==" }, "load-json-file": { "version": "2.0.0", @@ -5444,24 +19892,10 @@ "object-visit": "^1.0.0" } }, - "markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", - "requires": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - } - } + "mapbox-to-css-font": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/mapbox-to-css-font/-/mapbox-to-css-font-2.4.1.tgz", + "integrity": "sha512-QQ/iKiM43DM9+aujTL45Iz5o7gDeSFmy4LPl3HZmNcwCE++NxGazf+yFpY+wCb+YS23sDa1ghpo3zrNFOcHlow==" }, "math-expression-evaluator": { "version": "1.3.8", @@ -5486,11 +19920,6 @@ "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", "dev": true }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" - }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz", @@ -5816,8 +20245,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npm.taobao.org/minimist/download/minimist-1.2.5.tgz", - "integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=", - "dev": true + "integrity": "sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI=" }, "mississippi": { "version": "2.0.0", @@ -5889,7 +20317,8 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true }, "multicast-dns": { "version": "6.2.3", @@ -6103,6 +20532,11 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "numerify": { + "version": "1.2.9", + "resolved": "https://registry.npmmirror.com/numerify/-/numerify-1.2.9.tgz", + "integrity": "sha512-X4QzQiytV5ZN3TVLhzbtFzjTarUNnaa1pgNDFqt7u7Nqhxe7FvY2eYrGt4WYHlYXDqgtfC/n/a5nJ2y0LijV8w==" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", @@ -6211,6 +20645,27 @@ "integrity": "sha1-Cb6jND1BhZ69RGKS0RydTbYZCE4=", "dev": true }, + "ol": { + "version": "6.14.1", + "resolved": "https://registry.npmmirror.com/ol/-/ol-6.14.1.tgz", + "integrity": "sha512-sIcUWkGud3Y2gT3TJubSHlkyMXiPVh1yxfCPHxmY8+qtm79bB9oRnei9xHVIbRRG0Ro6Ldp5E+BMVSvYCxSpaA==", + "requires": { + "geotiff": "^2.0.2", + "ol-mapbox-style": "^7.1.1", + "pbf": "3.2.1", + "rbush": "^3.0.1" + } + }, + "ol-mapbox-style": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/ol-mapbox-style/-/ol-mapbox-style-7.1.1.tgz", + "integrity": "sha512-GLTEYiH/Ec9Zn1eS4S/zXyR2sierVrUc+OLVP8Ra0FRyqRhoYbXdko0b7OIeSHWdtJfHssWYefDOGxfTRUUZ/A==", + "requires": { + "@mapbox/mapbox-gl-style-spec": "^13.20.1", + "mapbox-to-css-font": "^2.4.1", + "webfont-matcher": "^1.1.0" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz", @@ -6428,6 +20883,11 @@ "safe-buffer": "^5.1.1" } }, + "parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==" + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npm.taobao.org/parse-json/download/parse-json-4.0.0.tgz?cache=0&sync_timestamp=1598130878813&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse-json%2Fdownload%2Fparse-json-4.0.0.tgz", @@ -6487,9 +20947,9 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npm.taobao.org/path-parse/download/path-parse-1.0.6.tgz", - "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-to-regexp": { @@ -6507,6 +20967,15 @@ "pify": "^3.0.0" } }, + "pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "requires": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + } + }, "pbkdf2": { "version": "3.1.1", "resolved": "https://registry.npm.taobao.org/pbkdf2/download/pbkdf2-3.1.1.tgz", @@ -8958,6 +23427,11 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npm.taobao.org/proxy-addr/download/proxy-addr-2.0.6.tgz", @@ -9069,6 +23543,16 @@ "integrity": "sha1-M0WUG0FTy50ILY7uTNogFqmu9/Y=", "dev": true }, + "quick-lru": { + "version": "6.1.1", + "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-6.1.1.tgz", + "integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==" + }, + "quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npm.taobao.org/randombytes/download/randombytes-2.1.0.tgz", @@ -9106,6 +23590,14 @@ "unpipe": "1.0.0" } }, + "rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "requires": { + "quickselect": "^2.0.0" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npm.taobao.org/read-cache/download/read-cache-1.0.0.tgz", @@ -9322,16 +23814,95 @@ "dev": true }, "renderkid": { - "version": "2.0.3", - "resolved": "https://registry.npm.taobao.org/renderkid/download/renderkid-2.0.3.tgz", - "integrity": "sha1-OAF5wv9a4TZcUivy/Pz/AcW3QUk=", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", "dev": true, "requires": { - "css-select": "^1.1.0", - "dom-converter": "^0.2", - "htmlparser2": "^3.3.0", - "strip-ansi": "^3.0.0", - "utila": "^0.4.0" + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "css-select": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", + "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "dev": true + }, + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + }, + "domhandler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + } } }, "repeat-element": { @@ -9381,8 +23952,8 @@ }, "resize-observer-polyfill": { "version": "1.5.1", - "resolved": "https://registry.nlark.com/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha1-DpAg3T0hAkRY1OvSfiPkAmmBBGQ=" + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" }, "resolve": { "version": "1.17.0", @@ -9408,6 +23979,14 @@ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true }, + "resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npm.taobao.org/resolve-url/download/resolve-url-0.2.1.tgz", @@ -9479,6 +24058,11 @@ "aproba": "^1.1.1" } }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz", @@ -9696,9 +24280,9 @@ "dev": true }, "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npm.taobao.org/shelljs/download/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "version": "0.8.5", + "resolved": "https://registry.npmmirror.com/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, "requires": { "glob": "^7.0.0", @@ -9857,6 +24441,14 @@ "requires": { "faye-websocket": "^0.10.0", "uuid": "^3.0.1" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } } }, "sockjs-client": { @@ -9884,6 +24476,16 @@ } } }, + "sort-asc": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/sort-asc/-/sort-asc-0.1.0.tgz", + "integrity": "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw==" + }, + "sort-desc": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/sort-desc/-/sort-desc-0.1.1.tgz", + "integrity": "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw==" + }, "sort-keys": { "version": "1.1.2", "resolved": "https://registry.npm.taobao.org/sort-keys/download/sort-keys-1.1.2.tgz", @@ -9893,6 +24495,15 @@ "is-plain-obj": "^1.0.0" } }, + "sort-object": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/sort-object/-/sort-object-0.3.2.tgz", + "integrity": "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA==", + "requires": { + "sort-asc": "^0.1.0", + "sort-desc": "^0.1.1" + } + }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npm.taobao.org/source-list-map/download/source-list-map-2.0.1.tgz", @@ -10056,7 +24667,8 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true }, "ssri": { "version": "5.3.0", @@ -10151,6 +24763,15 @@ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-2.1.1.tgz", @@ -10198,15 +24819,6 @@ "define-properties": "^1.1.3" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz?cache=0&sync_timestamp=1596697387823&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-3.0.1.tgz", @@ -10295,6 +24907,18 @@ "mkdirp": "~0.5.1", "sax": "~1.2.1", "whet.extend": "~0.9.9" + }, + "dependencies": { + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + } + } } }, "tapable": { @@ -10305,8 +24929,8 @@ }, "throttle-debounce": { "version": "1.1.0", - "resolved": "https://registry.npm.taobao.org/throttle-debounce/download/throttle-debounce-1.1.0.tgz", - "integrity": "sha1-UYU9o3vmihVctugns1FKPEIuic0=" + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-1.1.0.tgz", + "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==" }, "through2": { "version": "2.0.5", @@ -10451,11 +25075,6 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" - }, "uglify-js": { "version": "3.4.10", "resolved": "https://registry.npm.taobao.org/uglify-js/download/uglify-js-3.4.10.tgz?cache=0&sync_timestamp=1601823880483&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuglify-js%2Fdownload%2Fuglify-js-3.4.10.tgz", @@ -10719,9 +25338,9 @@ } }, "url-parse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz", - "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz", + "integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==", "dev": true, "requires": { "querystringify": "^2.1.1", @@ -10775,6 +25394,11 @@ "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", "dev": true }, + "utils-lite": { + "version": "0.1.10", + "resolved": "https://registry.npmmirror.com/utils-lite/-/utils-lite-0.1.10.tgz", + "integrity": "sha512-jlHvdtI8MyWURF/3u+ufIjf1Cs5WjN6WZl9qO8dEkZsVjaI7X5YMUhaCFzkvB69ljt6fo4Dd7V/Oj2NJOFDFOQ==" + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz", @@ -10782,10 +25406,21 @@ "dev": true }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npm.taobao.org/uuid/download/uuid-3.4.0.tgz?cache=0&sync_timestamp=1601826530476&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuuid%2Fdownload%2Fuuid-3.4.0.tgz", - "integrity": "sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=", - "dev": true + "version": "8.3.2", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "v-charts": { + "version": "1.19.0", + "resolved": "https://registry.npmmirror.com/v-charts/-/v-charts-1.19.0.tgz", + "integrity": "sha512-vm2HBUmxAsXK0ivwce9LytcpqrItDA5JSPLYVxZXtiuoyhcn80XX1/3dPJd/1GqG1OYv3jfBo1s9ra4q8GowqA==", + "requires": { + "echarts-amap": "1.0.0-rc.6", + "echarts-liquidfill": "^2.0.2", + "echarts-wordcloud": "^1.1.3", + "numerify": "1.2.9", + "utils-lite": "0.1.10" + } }, "validate-npm-package-license": { "version": "3.0.4", @@ -10820,18 +25455,6 @@ "resolved": "https://registry.npm.taobao.org/vue/download/vue-2.6.12.tgz?cache=0&sync_timestamp=1600441238751&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue%2Fdownload%2Fvue-2.6.12.tgz", "integrity": "sha1-9evU+mvShpQD4pqJau1JBEVskSM=" }, - "vue-baidu-map": { - "version": "0.21.22", - "resolved": "https://registry.npmjs.org/vue-baidu-map/-/vue-baidu-map-0.21.22.tgz", - "integrity": "sha512-WQMPCih4UTh0AZCKKH/OVOYnyAWjfRNeK6BIeoLmscyY5aF8zzlJhz/NOHLb3mdztIpB0Z6aohn4Jd9mfCSjQw==", - "requires": { - "bmaplib.curveline": "^1.0.0", - "bmaplib.heatmap": "^1.0.4", - "bmaplib.lushu": "^1.0.7", - "bmaplib.markerclusterer": "^1.0.13", - "markdown-it": "^8.4.0" - } - }, "vue-clipboard2": { "version": "0.3.1", "resolved": "https://registry.npm.taobao.org/vue-clipboard2/download/vue-clipboard2-0.3.1.tgz", @@ -10860,11 +25483,25 @@ } } }, + "vue-contextmenujs": { + "version": "1.3.13", + "resolved": "https://registry.npmmirror.com/vue-contextmenujs/download/vue-contextmenujs-1.3.13.tgz", + "integrity": "sha1-O9rgI8e9QgleeNpCWAACUNUKuO8=" + }, "vue-cookies": { "version": "1.7.4", "resolved": "https://registry.npm.taobao.org/vue-cookies/download/vue-cookies-1.7.4.tgz?cache=0&sync_timestamp=1598941352058&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-cookies%2Fdownload%2Fvue-cookies-1.7.4.tgz", "integrity": "sha1-0kHQoEMdoHlYN2UdELTXPnyNPo0=" }, + "vue-giant-tree": { + "version": "0.1.5", + "resolved": "https://registry.npmmirror.com/vue-giant-tree/-/vue-giant-tree-0.1.5.tgz", + "integrity": "sha512-P3KEHSZU2NkpWl6frss+sJLO0DLrtarMNLeTV/IGU2/w50rgrlKbKNr/ckK6BBVdWXAJYlYf6HUTNkKvGq5hlg==", + "requires": { + "@ztree/ztree_v3": "^3.5.44", + "jquery": "^3.5.1" + } + }, "vue-hot-reload-api": { "version": "2.3.4", "resolved": "https://registry.npm.taobao.org/vue-hot-reload-api/download/vue-hot-reload-api-2.3.4.tgz", @@ -10961,6 +25598,11 @@ "integrity": "sha1-HuO8mhbsv1EYvjNLsV+cRvgvWCU=", "dev": true }, + "vue-ztree-2.0": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/vue-ztree-2.0/-/vue-ztree-2.0-1.0.4.tgz", + "integrity": "sha512-d7KZsquEYpM0jD/k1uwOMFCd08L6++7zwRESaL2sF43OtRFCump8BxcLpjusBIHpFadPvOSMMnK5P41y+ZiTlA==" + }, "watchpack": { "version": "1.7.4", "resolved": "https://registry.npm.taobao.org/watchpack/download/watchpack-1.7.4.tgz?cache=0&sync_timestamp=1600385388649&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwatchpack%2Fdownload%2Fwatchpack-1.7.4.tgz", @@ -11164,6 +25806,16 @@ "minimalistic-assert": "^1.0.0" } }, + "web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" + }, + "webfont-matcher": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/webfont-matcher/-/webfont-matcher-1.1.0.tgz", + "integrity": "sha512-ov8lMvF9wi4PD7fK2Axn9PQEpO9cYI0fIoGqErwd+wi8xacFFDmX114D5Q2Lw0Wlgmb+Qw/dKI2KTtimrJf85g==" + }, "webpack": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", @@ -11834,6 +26486,11 @@ "safe-buffer": "~5.1.0" } }, + "xml-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/xml-utils/-/xml-utils-1.0.2.tgz", + "integrity": "sha512-rEn0FvKi+YGjv9omf22oAf+0d6Ly/sgJ/CUufU/nOzS7SRLmgwSujrewc03KojXxt+aPaTRpm593TgehtUBMSQ==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz?cache=0&sync_timestamp=1596697437792&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fxtend%2Fdownload%2Fxtend-4.0.2.tgz", diff --git a/web_src/package.json b/web_src/package.json index 69d8b6ab..18b3332c 100644 --- a/web_src/package.json +++ b/web_src/package.json @@ -10,19 +10,25 @@ "build": "node build/build.js" }, "dependencies": { - "axios": "^0.19.2", + "@liveqing/liveplayer": "^2.7.0", + "axios": "^0.24.0", "core-js": "^2.6.5", "echarts": "^4.9.0", - "element-ui": "^2.15.1", + "element-ui": "^2.15.6", "fingerprintjs2": "^2.1.2", "moment": "^2.29.1", + "ol": "^6.14.1", "postcss-pxtorem": "^5.1.1", + "uuid": "^8.3.2", + "v-charts": "^1.19.0", "vue": "^2.6.11", - "vue-baidu-map": "^0.21.22", "vue-clipboard2": "^0.3.1", "vue-clipboards": "^1.3.0", + "vue-contextmenujs": "^1.3.13", "vue-cookies": "^1.7.4", - "vue-router": "^3.1.6" + "vue-giant-tree": "^0.1.5", + "vue-router": "^3.1.6", + "vue-ztree-2.0": "^1.0.4" }, "devDependencies": { "autoprefixer": "^7.1.2", @@ -50,7 +56,7 @@ "postcss-url": "^7.2.1", "rimraf": "^2.6.0", "semver": "^5.3.0", - "shelljs": "^0.7.6", + "shelljs": "^0.8.5", "uglifyjs-webpack-plugin": "^1.1.1", "url-loader": "^0.5.8", "vue-loader": "^13.3.0", diff --git a/web_src/src/App.vue b/web_src/src/App.vue index e1145454..4ae7ea84 100644 --- a/web_src/src/App.vue +++ b/web_src/src/App.vue @@ -76,9 +76,33 @@ body, line-height: 60px; } .el-main { - background-color: #e9eef3; + background-color: #f0f2f5; color: #333; text-align: center; padding-top: 0px !important; } + +/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +/*定义滚动条轨道 内阴影+圆角*/ +::-webkit-scrollbar-track { + border-radius: 4px; + background-color: #F5F5F5; +} + +/*定义滑块 内阴影+圆角*/ +::-webkit-scrollbar-thumb { + border-radius: 4px; + background-color: #c8c8c8; + box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); +} +.table-header { + color: #727272; + font-weight: 600; +} diff --git a/web_src/src/components/CloudRecord.vue b/web_src/src/components/CloudRecord.vue index 78f8a46e..b046fc91 100644 --- a/web_src/src/components/CloudRecord.vue +++ b/web_src/src/components/CloudRecord.vue @@ -1,67 +1,59 @@ diff --git a/web_src/src/components/GBRecordDetail.vue b/web_src/src/components/GBRecordDetail.vue new file mode 100644 index 00000000..6fe29a89 --- /dev/null +++ b/web_src/src/components/GBRecordDetail.vue @@ -0,0 +1,513 @@ + + + + + + diff --git a/web_src/src/components/Login.vue b/web_src/src/components/Login.vue index d823659b..37c8a83a 100644 --- a/web_src/src/components/Login.vue +++ b/web_src/src/components/Login.vue @@ -66,10 +66,6 @@ export default { //登录请求 toLogin(){ - - //一般要跟后端了解密码的加密规则 - //这里例子用的哈希算法来自./js/sha1.min.js - //需要想后端发送的登录参数 let loginParam = { username: this.username, @@ -78,15 +74,20 @@ export default { var that = this; //设置在登录状态 this.isLoging = true; + let timeoutTask = setTimeout(()=>{ + that.$message.error("登录超时"); + that.isLoging = false; + }, 1000) this.$axios({ method: 'get', url:"/api/user/login", params: loginParam }).then(function (res) { + window.clearTimeout(timeoutTask) console.log(JSON.stringify(res)); - if (res.data.code == 0 && res.data.msg == "success") { - that.$cookies.set("session", {"username": that.username}) ; + if (res.data.code === 0 ) { + that.$cookies.set("session", {"username": that.username,"roleId":res.data.data.role.id}) ; //登录成功后 that.cancelEnterkeyDefaultAction(); that.$router.push('/'); @@ -99,6 +100,8 @@ export default { }); } }).catch(function (error) { + console.log(error) + window.clearTimeout(timeoutTask) that.$message.error(error.response.data.msg); that.isLoging = false; }); diff --git a/web_src/src/components/MediaServerManger.vue b/web_src/src/components/MediaServerManger.vue index c412bcb9..1d3c057c 100644 --- a/web_src/src/components/MediaServerManger.vue +++ b/web_src/src/components/MediaServerManger.vue @@ -1,45 +1,38 @@ + diff --git a/web_src/src/components/PushVideoList.vue b/web_src/src/components/PushVideoList.vue index d2d00e5e..6aed98a3 100644 --- a/web_src/src/components/PushVideoList.vue +++ b/web_src/src/components/PushVideoList.vue @@ -1,257 +1,353 @@ diff --git a/web_src/src/components/StreamProxyList.vue b/web_src/src/components/StreamProxyList.vue index 64e994a8..47ccde8b 100644 --- a/web_src/src/components/StreamProxyList.vue +++ b/web_src/src/components/StreamProxyList.vue @@ -1,107 +1,111 @@ @@ -109,7 +113,7 @@ import streamProxyEdit from './dialog/StreamProxyEdit.vue' import onvifEdit from './dialog/onvifEdit.vue' import devicePlayer from './dialog/devicePlayer.vue' - import uiHeader from './UiHeader.vue' + import uiHeader from '../layout/UiHeader.vue' export default { name: 'streamProxyList', components: { @@ -128,15 +132,14 @@ currentPage:1, count:15, total:0, - getListLoading: false, - startBtnLaoding: false + startBtnLoading: false }; }, computed: { }, mounted() { this.initData(); - this.updateLooper = setInterval(this.initData, 1000); + this.startUpdateList() }, destroyed() { this.$destroy('videojs'); @@ -146,6 +149,12 @@ initData: function() { this.getStreamProxyList(); }, + stopUpdateList: function (){ + window.clearInterval(this.updateLooper) + }, + startUpdateList: function (){ + this.updateLooper = setInterval(this.initData, 1000); + }, currentChange: function(val){ this.currentPage = val; this.getStreamProxyList(); @@ -156,7 +165,6 @@ }, getStreamProxyList: function() { let that = this; - this.getListLoading = true; this.$axios({ method: 'get', url:`/api/proxy/list`, @@ -165,26 +173,26 @@ count: that.count } }).then(function (res) { - that.total = res.data.total; - that.streamProxyList = res.data.list; - that.getListLoading = false; + if (res.data.code === 0) { + that.total = res.data.data.total; + for (let i = 0; i < res.data.data.list.length; i++) { + res.data.data.list[i]["startBtnLoading"] = false; + } + that.streamProxyList = res.data.data.list; + } }).catch(function (error) { console.log(error); - that.getListLoading = false; }); }, addStreamProxy: function(){ this.$refs.streamProxyEdit.openDialog(null, this.initData) }, addOnvif: function(){ - this.getListLoading = true; - this.getListLoading = true; this.$axios({ method: 'get', url:`/api/onvif/search?timeout=3000`, }).then((res) =>{ - this.getListLoading = false; - if (res.data.code == 0 ){ + if (res.data.code === 0 ){ if (res.data.data.length > 0) { this.$refs.onvifEdit.openDialog(res.data.data, (url)=>{ if (url != null) { @@ -200,7 +208,6 @@ } }).catch((error)=> { - this.getListLoading = false; this.$message.error(error.response.data.msg); }); @@ -209,17 +216,15 @@ }, play: function(row){ let that = this; - this.getListLoading = true; this.$axios({ method: 'get', - url:`/api/media/stream_info_by_app_and_stream`, + url:`/api/push/getPlayUrl`, params: { app: row.app, stream: row.stream, mediaServerId: row.mediaServerId } }).then(function (res) { - that.getListLoading = false; if (res.data.code === 0) { that.$refs.devicePlayer.openDialog("streamPlay", null, null, { streamInfo: res.data.data, @@ -235,13 +240,11 @@ }).catch(function (error) { console.log(error); - that.getListLoading = false; }); }, deleteStreamProxy: function(row){ let that = this; - this.getListLoading = true; that.$axios({ method:"delete", url:"/api/proxy/del", @@ -250,17 +253,14 @@ stream: row.stream } }).then((res)=>{ - that.getListLoading = false; that.initData() }).catch(function (error) { console.log(error); - that.getListLoading = false; }); }, start: function(row){ - let that = this; - this.getListLoading = true; - this.startBtnLaoding = true; + this.stopUpdateList() + this.$set(row, 'startBtnLoading', true) this.$axios({ method: 'get', url:`/api/proxy/start`, @@ -268,28 +268,31 @@ app: row.app, stream: row.stream } - }).then(function (res) { - that.getListLoading = false; - that.startBtnLaoding = false; - if (res.data == "success"){ - that.initData() + }).then((res)=> { + if (res.data.code === 0){ + this.initData() }else { - that.$message({ + this.$message({ showClose: true, - message: "保存失败,请检查地址是否可用!", + message: "启动失败,请检查地址是否可用!", type: "error", }); } - - }).catch(function (error) { + this.$set(row, 'startBtnLoading', false) + this.startUpdateList() + }).catch((error)=> { console.log(error); - that.getListLoading = false; - that.startBtnLaoding = false; + this.$message({ + showClose: true, + message: "启动失败,请检查地址是否可用!", + type: "error", + }); + this.$set(row, 'startBtnLoading', false) + this.startUpdateList() }); }, stop: function(row){ let that = this; - this.getListLoading = true; this.$axios({ method: 'get', url:`/api/proxy/stop`, @@ -298,14 +301,14 @@ stream: row.stream } }).then(function (res) { - that.getListLoading = false; that.initData() }).catch(function (error) { console.log(error); - that.getListLoading = false; }); - } - + }, + refresh: function (){ + this.initData(); + } } }; diff --git a/web_src/src/components/UiHeader.vue b/web_src/src/components/UiHeader.vue deleted file mode 100644 index 6391fe8c..00000000 --- a/web_src/src/components/UiHeader.vue +++ /dev/null @@ -1,140 +0,0 @@ - - - diff --git a/web_src/src/components/UserManager.vue b/web_src/src/components/UserManager.vue new file mode 100644 index 00000000..c0fa695a --- /dev/null +++ b/web_src/src/components/UserManager.vue @@ -0,0 +1,238 @@ + + + + diff --git a/web_src/src/components/channelList.vue b/web_src/src/components/channelList.vue index 2b068709..f020f345 100644 --- a/web_src/src/components/channelList.vue +++ b/web_src/src/components/channelList.vue @@ -1,392 +1,479 @@ diff --git a/web_src/src/components/common/ h265web.vue b/web_src/src/components/common/ h265web.vue new file mode 100644 index 00000000..4049721c --- /dev/null +++ b/web_src/src/components/common/ h265web.vue @@ -0,0 +1,327 @@ + + + + + diff --git a/web_src/src/components/common/DeviceTree.vue b/web_src/src/components/common/DeviceTree.vue new file mode 100644 index 00000000..c701bf0f --- /dev/null +++ b/web_src/src/components/common/DeviceTree.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/web_src/src/components/common/DeviceTreeForZtree.vue b/web_src/src/components/common/DeviceTreeForZtree.vue new file mode 100644 index 00000000..6c02bcca --- /dev/null +++ b/web_src/src/components/common/DeviceTreeForZtree.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/web_src/src/components/common/MapComponent.vue b/web_src/src/components/common/MapComponent.vue new file mode 100644 index 00000000..fb091ad6 --- /dev/null +++ b/web_src/src/components/common/MapComponent.vue @@ -0,0 +1,263 @@ + + + + + diff --git a/web_src/src/components/common/jessibuca.vue b/web_src/src/components/common/jessibuca.vue new file mode 100644 index 00000000..4049721c --- /dev/null +++ b/web_src/src/components/common/jessibuca.vue @@ -0,0 +1,327 @@ + + + + + diff --git a/web_src/src/components/console.vue b/web_src/src/components/console.vue new file mode 100644 index 00000000..e192fb1b --- /dev/null +++ b/web_src/src/components/console.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/web_src/src/components/console/ConsoleCPU.vue b/web_src/src/components/console/ConsoleCPU.vue new file mode 100644 index 00000000..5aed07ed --- /dev/null +++ b/web_src/src/components/console/ConsoleCPU.vue @@ -0,0 +1,109 @@ + + + diff --git a/web_src/src/components/console/ConsoleDisk.vue b/web_src/src/components/console/ConsoleDisk.vue new file mode 100644 index 00000000..ed69cc3f --- /dev/null +++ b/web_src/src/components/console/ConsoleDisk.vue @@ -0,0 +1,81 @@ + + + diff --git a/web_src/src/components/console/ConsoleMEM.vue b/web_src/src/components/console/ConsoleMEM.vue new file mode 100644 index 00000000..566b469b --- /dev/null +++ b/web_src/src/components/console/ConsoleMEM.vue @@ -0,0 +1,103 @@ + + + diff --git a/web_src/src/components/console/ConsoleMediaServer.vue b/web_src/src/components/console/ConsoleMediaServer.vue new file mode 100644 index 00000000..a842b503 --- /dev/null +++ b/web_src/src/components/console/ConsoleMediaServer.vue @@ -0,0 +1,85 @@ + + + diff --git a/web_src/src/components/console/ConsoleNet.vue b/web_src/src/components/console/ConsoleNet.vue new file mode 100644 index 00000000..22d4f343 --- /dev/null +++ b/web_src/src/components/console/ConsoleNet.vue @@ -0,0 +1,89 @@ + + + diff --git a/web_src/src/components/console/ConsoleNodeLoad.vue b/web_src/src/components/console/ConsoleNodeLoad.vue new file mode 100644 index 00000000..0596c41e --- /dev/null +++ b/web_src/src/components/console/ConsoleNodeLoad.vue @@ -0,0 +1,63 @@ + + + diff --git a/web_src/src/components/console/ConsoleResource.vue b/web_src/src/components/console/ConsoleResource.vue new file mode 100644 index 00000000..3aa77715 --- /dev/null +++ b/web_src/src/components/console/ConsoleResource.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/web_src/src/components/control.vue b/web_src/src/components/control.vue deleted file mode 100644 index b5990dab..00000000 --- a/web_src/src/components/control.vue +++ /dev/null @@ -1,442 +0,0 @@ - - - - - diff --git a/web_src/src/components/devicePosition.vue b/web_src/src/components/devicePosition.vue deleted file mode 100644 index 777b11ed..00000000 --- a/web_src/src/components/devicePosition.vue +++ /dev/null @@ -1,391 +0,0 @@ - - - - - diff --git a/web_src/src/components/dialog/MediaServerEdit.vue b/web_src/src/components/dialog/MediaServerEdit.vue index 7206bace..9353a811 100644 --- a/web_src/src/components/dialog/MediaServerEdit.vue +++ b/web_src/src/components/dialog/MediaServerEdit.vue @@ -41,10 +41,6 @@ - - - - @@ -74,6 +70,10 @@ + + + + @@ -89,14 +89,6 @@ - - - - - - - - - - @@ -172,7 +164,6 @@ export default { hookIp: "", sdpIp: "", streamIp: "", - streamNoneReaderDelayMS: "", secret: "035c73f7-bb6b-4889-a715-d9eb2d1925cc", httpPort: "", httpSSlPort: "", @@ -181,15 +172,12 @@ export default { rtmpSSlPort: "", rtpEnable: false, rtpPortRange: "", - sendRtpPortRange: "", rtpProxyPort: "", rtspPort: "", rtspSSLPort: "", }, rtpPortRange1:30000, rtpPortRange2:30500, - sendRtpPortRange1:30000, - sendRtpPortRange2:30500, rules: { ip: [{ required: true, validator: isValidIp, message: '请输入有效的IP地址', trigger: 'blur' }], @@ -200,8 +188,6 @@ export default { rtmpSSlPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtpPortRange2: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], - sendRtpPortRange1: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], - sendRtpPortRange2: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtpProxyPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtspPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], rtspSSLPort: [{ required: true, validator: isValidPort, message: '请输入有效的端口号', trigger: 'blur' }], @@ -233,9 +219,6 @@ export default { this.rtpPortRange2 = rtpPortRange[1] } } - let sendRtpPortRange = this.mediaServerForm.sendRtpPortRange.split(","); - this.sendRtpPortRange1 = sendRtpPortRange[0] - this.sendRtpPortRange2 = sendRtpPortRange[1] } }, checkServer: function() { @@ -255,8 +238,6 @@ export default { that.mediaServerForm = data.data; that.mediaServerForm.httpPort = httpPort; that.mediaServerForm.autoConfig = true; - that.sendRtpPortRange1 = 30000 - that.sendRtpPortRange2 = 30500 that.rtpPortRange1 = 30000 that.rtpPortRange2 = 30500 that.serverCheck = 1; @@ -332,7 +313,6 @@ export default { hookIp: "", sdpIp: "", streamIp: "", - streamNoneReaderDelayMS: "", secret: "035c73f7-bb6b-4889-a715-d9eb2d1925cc", httpPort: "", httpSSlPort: "", @@ -341,13 +321,10 @@ export default { rtmpSSlPort: "", rtpEnable: false, rtpPortRange: "", - sendRtpPortRange: "", rtpProxyPort: "", rtspPort: "", rtspSSLPort: "", }; - this.sendRtpPortRange1 = 30000; - this.sendRtpPortRange2 = 30500; this.rtpPortRange1 = 30500; this.rtpPortRange2 = 30500; this.listChangeCallback = null @@ -357,7 +334,7 @@ export default { var result = false; var that = this; await that.$axios({ - method: 'post', + method: 'get', url:`/api/platform/exit/${deviceGbId}` }).then(function (res) { result = res.data; @@ -372,9 +349,7 @@ export default { } }, portRangeChange: function() { - this.mediaServerForm.sendRtpPortRange = this.sendRtpPortRange1 + "," + this.sendRtpPortRange2 this.mediaServerForm.rtpPortRange = this.rtpPortRange1 + "," + this.rtpPortRange2 - console.log(this.mediaServerForm.sendRtpPortRange) console.log(this.mediaServerForm.rtpPortRange) } }, diff --git a/web_src/src/components/dialog/StreamProxyEdit.vue b/web_src/src/components/dialog/StreamProxyEdit.vue index fa09cf8d..76011fac 100644 --- a/web_src/src/components/dialog/StreamProxyEdit.vue +++ b/web_src/src/components/dialog/StreamProxyEdit.vue @@ -46,7 +46,6 @@ style="width: 100%" placeholder="请选择拉流节点" > - - - - - - {{ item.name }} - {{ item.serverGBId }} - - - + + + + + + +
- - - + +
@@ -168,11 +161,13 @@ export default { gbId: null, rtp_type: null, enable: true, - enable_hls: true, + enable_audio: true, enable_mp4: false, + none_reader: null, enable_remove_none_reader: false, + enable_disable_none_reader: false, platformGbId: null, - mediaServerId: "auto", + mediaServerId: null, }, mediaServerList:{}, ffmpegCmdList:{}, @@ -194,19 +189,22 @@ export default { this.listChangeCallback = callback; if (proxyParam != null) { this.proxyParam = proxyParam; + this.proxyParam.none_reader = null; } let that = this; this.$axios({ method: 'get', - url:`/api/platform/query/10000/0` + url:`/api/platform/query/10000/1` }).then(function (res) { - that.platformList = res.data.list; + that.platformList = res.data.data.list; }).catch(function (error) { console.log(error); }); this.mediaServer.getOnlineMediaServerList((data)=>{ - this.mediaServerList = data; + this.mediaServerList = data.data; + this.proxyParam.mediaServerId = this.mediaServerList[0].id + this.mediaServerIdChange() }) }, mediaServerIdChange:function (){ @@ -220,6 +218,7 @@ export default { } }).then(function (res) { that.ffmpegCmdList = res.data.data; + that.proxyParam.ffmpeg_cmd_key = Object.keys(res.data.data)[0]; }).catch(function (error) { console.log(error); }); @@ -228,26 +227,26 @@ export default { }, onSubmit: function () { this.dialogLoading = true; - var that = this; - that.$axios({ + this.noneReaderHandler(); + this.$axios({ method: 'post', url:`/api/proxy/save`, - data: that.proxyParam - }).then(function (res) { - that.dialogLoading = false; + data: this.proxyParam + }).then((res)=> { + this.dialogLoading = false; if (typeof (res.data.code) != "undefined" && res.data.code === 0) { - that.$message({ + this.$message({ showClose: true, message: res.data.msg, type: "success", }); - that.showDialog = false; - if (that.listChangeCallback != null) { - that.listChangeCallback(); - that.dialogLoading = false; + this.showDialog = false; + if (this.listChangeCallback != null) { + this.listChangeCallback(); + this.dialogLoading = false; } } - }).catch(function (error) { + }).catch((error) =>{ console.log(error); this.dialogLoading = false; }); @@ -261,7 +260,7 @@ export default { var result = false; var that = this; await that.$axios({ - method: 'post', + method: 'get', url:`/api/platform/exit/${deviceGbId}` }).then(function (res) { result = res.data; @@ -274,7 +273,19 @@ export default { if (this.platform.enable && this.platform.expires == "0") { this.platform.expires = "300"; } - } + }, + noneReaderHandler: function() { + if (this.proxyParam.none_reader === null || this.proxyParam.none_reader === "0") { + this.proxyParam.enable_disable_none_reader = false; + this.proxyParam.enable_remove_none_reader = false; + }else if (this.proxyParam.none_reader === "1"){ + this.proxyParam.enable_disable_none_reader = true; + this.proxyParam.enable_remove_none_reader = false; + }else if (this.proxyParam.none_reader ==="2"){ + this.proxyParam.enable_disable_none_reader = false; + this.proxyParam.enable_remove_none_reader = true; + } + }, }, }; diff --git a/web_src/src/components/dialog/SyncChannelProgress.vue b/web_src/src/components/dialog/SyncChannelProgress.vue new file mode 100644 index 00000000..e1c9fe01 --- /dev/null +++ b/web_src/src/components/dialog/SyncChannelProgress.vue @@ -0,0 +1,112 @@ + + + diff --git a/web_src/src/components/dialog/addUser.vue b/web_src/src/components/dialog/addUser.vue new file mode 100644 index 00000000..8dc56827 --- /dev/null +++ b/web_src/src/components/dialog/addUser.vue @@ -0,0 +1,154 @@ + + + diff --git a/web_src/src/components/dialog/catalogEdit.vue b/web_src/src/components/dialog/catalogEdit.vue new file mode 100644 index 00000000..e1cd8d26 --- /dev/null +++ b/web_src/src/components/dialog/catalogEdit.vue @@ -0,0 +1,149 @@ + + + diff --git a/web_src/src/components/dialog/changePassword.vue b/web_src/src/components/dialog/changePassword.vue index a95736f2..77e1d2a8 100644 --- a/web_src/src/components/dialog/changePassword.vue +++ b/web_src/src/components/dialog/changePassword.vue @@ -75,7 +75,10 @@ export default { isLoging: false, rules: { oldPassword: [{ required: true, validator: validatePass0, trigger: "blur" }], - newPassword: [{ required: true, validator: validatePass1, trigger: "blur" }], + newPassword: [{ required: true, validator: validatePass1, trigger: "blur" }, { + pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,.\/]).{8,20}$/, + message: "密码长度在8-20位之间,由字母+数字+特殊字符组成", + },], confirmPassword: [{ required: true, validator: validatePass2, trigger: "blur" }], }, }; @@ -93,7 +96,7 @@ export default { password: this.newPassword } }).then((res)=> { - if (res.data === "success"){ + if (res.data.code === 0) { this.$message({ showClose: true, message: '修改成功,请重新登录', diff --git a/web_src/src/components/dialog/changePasswordForAdmin.vue b/web_src/src/components/dialog/changePasswordForAdmin.vue new file mode 100644 index 00000000..5b913577 --- /dev/null +++ b/web_src/src/components/dialog/changePasswordForAdmin.vue @@ -0,0 +1,121 @@ + + + diff --git a/web_src/src/components/dialog/changePushKey.vue b/web_src/src/components/dialog/changePushKey.vue new file mode 100644 index 00000000..0b9834ef --- /dev/null +++ b/web_src/src/components/dialog/changePushKey.vue @@ -0,0 +1,101 @@ + + + diff --git a/web_src/src/components/dialog/channelMapInfobox.vue b/web_src/src/components/dialog/channelMapInfobox.vue new file mode 100644 index 00000000..2ef0e529 --- /dev/null +++ b/web_src/src/components/dialog/channelMapInfobox.vue @@ -0,0 +1,65 @@ + + + diff --git a/web_src/src/components/dialog/chooseChannel.vue b/web_src/src/components/dialog/chooseChannel.vue index 87fc62e4..e0e79c3a 100644 --- a/web_src/src/components/dialog/chooseChannel.vue +++ b/web_src/src/components/dialog/chooseChannel.vue @@ -1,25 +1,40 @@ @@ -27,39 +42,50 @@ diff --git a/web_src/src/components/dialog/chooseChannelForCatalog.vue b/web_src/src/components/dialog/chooseChannelForCatalog.vue new file mode 100644 index 00000000..c634b77d --- /dev/null +++ b/web_src/src/components/dialog/chooseChannelForCatalog.vue @@ -0,0 +1,316 @@ + + + + + + diff --git a/web_src/src/components/dialog/chooseChannelForGb.vue b/web_src/src/components/dialog/chooseChannelForGb.vue index eddcdf9f..fc97b4ce 100644 --- a/web_src/src/components/dialog/chooseChannelForGb.vue +++ b/web_src/src/components/dialog/chooseChannelForGb.vue @@ -1,6 +1,10 @@ + + diff --git a/web_src/src/components/dialog/importChannel.vue b/web_src/src/components/dialog/importChannel.vue new file mode 100644 index 00000000..91611e80 --- /dev/null +++ b/web_src/src/components/dialog/importChannel.vue @@ -0,0 +1,125 @@ + + + + diff --git a/web_src/src/components/dialog/importChannelShowErrorData.vue b/web_src/src/components/dialog/importChannelShowErrorData.vue new file mode 100644 index 00000000..5194b7ee --- /dev/null +++ b/web_src/src/components/dialog/importChannelShowErrorData.vue @@ -0,0 +1,64 @@ + + + + diff --git a/web_src/src/components/dialog/jessibuca.vue b/web_src/src/components/dialog/jessibuca.vue deleted file mode 100644 index 7e9e0097..00000000 --- a/web_src/src/components/dialog/jessibuca.vue +++ /dev/null @@ -1,298 +0,0 @@ - - - - - diff --git a/web_src/src/components/dialog/onvifEdit.vue b/web_src/src/components/dialog/onvifEdit.vue index c8532947..17eabb39 100644 --- a/web_src/src/components/dialog/onvifEdit.vue +++ b/web_src/src/components/dialog/onvifEdit.vue @@ -90,7 +90,7 @@ export default { } }).then((res) => { console.log(res.data) - if (res.data.code == 0) { + if (res.data.code === 0) { if (res.data.data != null) { this.listChangeCallback(res.data.data) }else { diff --git a/web_src/src/components/dialog/platformEdit.vue b/web_src/src/components/dialog/platformEdit.vue index fad0444e..76382324 100644 --- a/web_src/src/components/dialog/platformEdit.vue +++ b/web_src/src/components/dialog/platformEdit.vue @@ -37,12 +37,15 @@ + + + - - + + @@ -63,6 +66,24 @@ + + + + + + + + + + + + + + - + + {{ @@ -116,6 +138,7 @@ export default { showDialog: false, isLoging: false, onSubmit_text: "立即创建", + saveUrl: "/api/platform/save", platform: { id: null, @@ -136,7 +159,10 @@ export default { keepTimeout: 60, transport: "UDP", characterSet: "GB2312", - shareAllLiveStream: false, + startOfflinePush: false, + catalogGroup: 1, + administrativeDivision: null, + treeType: "BusinessGroup", }, rules: { name: [{ required: true, message: "请输入平台名称", trigger: "blur" }], @@ -163,16 +189,22 @@ export default { var that = this; if (platform == null) { this.onSubmit_text = "立即创建"; + this.saveUrl = "/api/platform/add"; this.$axios({ method: 'get', url:`/api/platform/server_config` }).then(function (res) { console.log(res); - that.platform.deviceGBId = res.data.username; - that.platform.deviceIp = res.data.deviceIp; - that.platform.devicePort = res.data.devicePort; - that.platform.username = res.data.username; - that.platform.password = res.data.password; + if (res.data.code === 0) { + that.platform.deviceGBId = res.data.data.username; + that.platform.deviceIp = res.data.data.deviceIp; + that.platform.devicePort = res.data.data.devicePort; + that.platform.username = res.data.data.username; + that.platform.password = res.data.data.password; + that.platform.treeType = "BusinessGroup"; + that.platform.administrativeDivision = res.data.data.username.substr(0, 6); + } + }).catch(function (error) { console.log(error); }); @@ -195,8 +227,13 @@ export default { this.platform.keepTimeout = platform.keepTimeout; this.platform.transport = platform.transport; this.platform.characterSet = platform.characterSet; - this.platform.shareAllLiveStream = platform.shareAllLiveStream; + this.platform.catalogId = platform.catalogId; + this.platform.startOfflinePush = platform.startOfflinePush; + this.platform.catalogGroup = platform.catalogGroup; + this.platform.administrativeDivision = platform.administrativeDivision; + this.platform.treeType = platform.treeType; this.onSubmit_text = "保存"; + this.saveUrl = "/api/platform/save"; } this.showDialog = true; this.listChangeCallback = callback; @@ -209,29 +246,40 @@ export default { deviceGBIdChange: function () { this.platform.username = this.platform.deviceGBId ; + if (this.platform.administrativeDivision == null) { + this.platform.administrativeDivision = this.platform.deviceGBId.substr(0, 6); + } + }, onSubmit: function () { - console.log("onSubmit"); - var that = this; - that.$axios({ + this.saveForm() + }, + saveForm: function (){ + this.$axios({ method: 'post', - url:`/api/platform/save`, - data: that.platform - }).then(function (res) { - if (res.data == "success") { - that.$message({ - showClose: true, - message: "保存成功", - type: "success", - }); - that.showDialog = false; - if (that.listChangeCallback != null) { - that.listChangeCallback(); - } + url: this.saveUrl, + data: this.platform + }).then((res) =>{ + if (res.data.code === 0) { + this.$message({ + showClose: true, + message: "保存成功", + type: "success", + }); + this.showDialog = false; + if (this.listChangeCallback != null) { + this.listChangeCallback(); } - }).catch(function (error) { - console.log(error); - }); + }else { + this.$message({ + showClose: true, + message: res.data.msg, + type: "error", + }); + } + }).catch((error)=> { + console.log(error); + }); }, close: function () { this.showDialog = false; @@ -244,6 +292,7 @@ export default { rtcp: false, name: null, serverGBId: null, + administrativeDivision: null, serverGBDomain: null, serverIP: null, serverPort: null, @@ -256,17 +305,21 @@ export default { keepTimeout: 60, transport: "UDP", characterSet: "GB2312", - shareAllLiveStream: false, + treeType: "BusinessGroup", + startOfflinePush: false, + catalogGroup: 1, } }, deviceGBIdExit: async function (deviceGbId) { var result = false; var that = this; await that.$axios({ - method: 'post', + method: 'get', url:`/api/platform/exit/${deviceGbId}`}) .then(function (res) { - result = res.data; + if (res.data.code === 0) { + result = res.data.data; + } }) .catch(function (error) { console.log(error); @@ -277,6 +330,22 @@ export default { if (this.platform.enable && this.platform.expires == "0") { this.platform.expires = "300"; } + }, + rtcpCheckBoxChange: function (result){ + if (result) { + this.$message({ + showClose: true, + message: "开启RTCP保活需要上级平台支持,可以避免无效推流", + type: "warning", + }); + } + }, + treeTypeChange: function (){ + this.$message({ + showClose: true, + message: "修改目录结构会导致关联目录与通道数据被清空,保存后生效", + type: "warning", + }); } }, }; diff --git a/web_src/src/components/dialog/addStreamTOGB.vue b/web_src/src/components/dialog/pushStreamEdit.vue similarity index 66% rename from web_src/src/components/dialog/addStreamTOGB.vue rename to web_src/src/components/dialog/pushStreamEdit.vue index 9baf1bc1..de4e7bcc 100644 --- a/web_src/src/components/dialog/addStreamTOGB.vue +++ b/web_src/src/components/dialog/pushStreamEdit.vue @@ -15,20 +15,25 @@ - + - + + + + + + +
保存 取消
-
@@ -38,7 +43,7 @@ diff --git a/web_src/src/components/dialog/recordDownload.vue b/web_src/src/components/dialog/recordDownload.vue new file mode 100644 index 00000000..3e8c4271 --- /dev/null +++ b/web_src/src/components/dialog/recordDownload.vue @@ -0,0 +1,200 @@ + + + + + + diff --git a/web_src/src/components/dialog/rtcPlayer.vue b/web_src/src/components/dialog/rtcPlayer.vue index 75c18f33..f957df71 100644 --- a/web_src/src/components/dialog/rtcPlayer.vue +++ b/web_src/src/components/dialog/rtcPlayer.vue @@ -7,11 +7,11 @@ + + diff --git a/web_src/src/components/map.vue b/web_src/src/components/map.vue new file mode 100644 index 00000000..2aa17f63 --- /dev/null +++ b/web_src/src/components/map.vue @@ -0,0 +1,393 @@ + + + + + diff --git a/web_src/src/components/service/DeviceService.js b/web_src/src/components/service/DeviceService.js new file mode 100644 index 00000000..85d36f8b --- /dev/null +++ b/web_src/src/components/service/DeviceService.js @@ -0,0 +1,177 @@ +import axios from 'axios'; + +class DeviceService{ + + constructor() { + this.$axios = axios; + } + + getDeviceList(currentPage, count, callback, errorCallback){ + this.$axios({ + method: 'get', + url:`/api/device/query/devices`, + params: { + page: currentPage, + count: count + } + }).then((res) => { + if (typeof (callback) == "function") callback(res.data) + }).catch((error) => { + console.log(error); + if (typeof (errorCallback) == "function") errorCallback(error) + }); + } + + getDevice(deviceId, callback, errorCallback){ + this.$axios({ + method: 'get', + url:`/api/device/query/devices/${deviceId}`, + }).then((res) => { + if (typeof (callback) == "function") callback(res.data) + }).catch((error) => { + console.log(error); + if (typeof (errorCallback) == "function") errorCallback(error) + }); + } + + getAllDeviceList(callback,endCallback, errorCallback) { + let currentPage = 1; + let count = 100; + let deviceList = [] + this.getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback) + } + + getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback) { + this.getDeviceList(currentPage, count, (data) => { + if (data.code === 0 && data.data.list) { + if (typeof (callback) == "function") callback(data.data.list) + deviceList = deviceList.concat(data.data.list); + if (deviceList.length < data.data.total) { + currentPage ++ + this.getAllDeviceListIteration(deviceList, currentPage, count, callback, endCallback, errorCallback) + }else { + if (typeof (endCallback) == "function") endCallback(deviceList) + } + } + }, errorCallback) + } + + + getAllChannel(isCatalog, catalogUnderDevice, deviceId, callback, endCallback, errorCallback) { + let currentPage = 1; + let count = 100; + let catalogList = [] + this.getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, endCallback, errorCallback) + } + + getAllChannelIteration(isCatalog, catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, endCallback, errorCallback) { + this.getChanel(isCatalog, catalogUnderDevice, deviceId, currentPage, count, (data) => { + if (data.list) { + if (typeof (callback) == "function") callback(data.list) + catalogList = catalogList.concat(data.list); + if (catalogList.length < data.total) { + currentPage ++ + this.getAllChannelIteration(isCatalog,catalogUnderDevice, deviceId, catalogList, currentPage, count, callback, errorCallback) + }else { + console.log(1) + if (typeof (endCallback) == "function") endCallback(catalogList) + } + } + }, errorCallback) + } + getChanel(isCatalog, catalogUnderDevice, deviceId, currentPage, count, callback, errorCallback) { + this.$axios({ + method: 'get', + url: `/api/device/query/devices/${deviceId}/channels`, + params:{ + page: currentPage, + count: count, + query: "", + online: "", + channelType: isCatalog, + catalogUnderDevice: catalogUnderDevice + } + }).then((res) =>{ + if (typeof (callback) == "function") callback(res.data) + }).catch(errorCallback); + } + + + getAllSubChannel(isCatalog, deviceId, channelId, callback, endCallback, errorCallback) { + let currentPage = 1; + let count = 100; + let catalogList = [] + this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, endCallback, errorCallback) + } + + getAllSubChannelIteration(isCatalog, deviceId,channelId, catalogList, currentPage, count, callback, endCallback, errorCallback) { + this.getSubChannel(isCatalog, deviceId, channelId, currentPage, count, (data) => { + if (data.list) { + if (typeof (callback) == "function") callback(data.list) + catalogList = catalogList.concat(data.list); + if (catalogList.length < data.total) { + currentPage ++ + this.getAllSubChannelIteration(isCatalog, deviceId, channelId, catalogList, currentPage, count, callback, endCallback, errorCallback) + }else { + if (typeof (endCallback) == "function") endCallback(catalogList) + } + } + }, errorCallback) + } + getSubChannel(isCatalog, deviceId, channelId, currentPage, count, callback, errorCallback) { + this.$axios({ + method: 'get', + url: `/api/device/query/sub_channels/${deviceId}/${channelId}/channels`, + params:{ + page: currentPage, + count: count, + query: "", + online: "", + channelType: isCatalog + } + }).then((res) =>{ + if (typeof (callback) == "function") callback(res.data) + }).catch(errorCallback); + } + + getTree(deviceId, parentId, onlyCatalog, callback, endCallback, errorCallback) { + let currentPage = 1; + let count = 100; + let catalogList = [] + this.getTreeIteration(deviceId, parentId, onlyCatalog, catalogList, currentPage, count, callback, endCallback, errorCallback) + } + + getTreeIteration(deviceId, parentId, onlyCatalog, catalogList, currentPage, count, callback, endCallback, errorCallback) { + this.getTreeInfo(deviceId, parentId, onlyCatalog, currentPage, count, (data) => { + if (data.code === 0 && data.data.list) { + if (typeof (callback) == "function") callback(data.data.list) + catalogList = catalogList.concat(data.data.list); + if (catalogList.length < data.data.total) { + currentPage ++ + this.getTreeIteration(deviceId, parentId, onlyCatalog, catalogList, currentPage, count, callback, endCallback, errorCallback) + }else { + if (typeof (endCallback) == "function") endCallback(catalogList) + } + } + }, errorCallback) + } + getTreeInfo(deviceId, parentId, onlyCatalog, currentPage, count, callback, errorCallback) { + if (onlyCatalog == null || typeof onlyCatalog === "undefined") { + onlyCatalog = false; + } + this.$axios({ + method: 'get', + url: `/api/device/query/tree/${deviceId}`, + params:{ + page: currentPage, + count: count, + parentId: parentId, + onlyCatalog: onlyCatalog + } + }).then((res) =>{ + if (typeof (callback) == "function") callback(res.data) + }).catch(errorCallback); + } +} + +export default DeviceService; diff --git a/web_src/src/components/service/MediaServer.js b/web_src/src/components/service/MediaServer.js index 36474ead..d4446f06 100644 --- a/web_src/src/components/service/MediaServer.js +++ b/web_src/src/components/service/MediaServer.js @@ -10,9 +10,9 @@ class MediaServer{ this.$axios({ method: 'get', url:`/api/server/media_server/online/list`, - }).then(function (res) { + }).then((res) => { if (typeof (callback) == "function") callback(res.data) - }).catch(function (error) { + }).catch((error) => { console.log(error); }); } diff --git a/web_src/src/components/setting/Media.vue b/web_src/src/components/setting/Media.vue index cfc4e12a..66426010 100644 --- a/web_src/src/components/setting/Media.vue +++ b/web_src/src/components/setting/Media.vue @@ -1,85 +1,75 @@ - - diff --git a/web_src/src/components/test2.vue b/web_src/src/components/test2.vue deleted file mode 100644 index 75f182eb..00000000 --- a/web_src/src/components/test2.vue +++ /dev/null @@ -1,190 +0,0 @@ - - - - - diff --git a/web_src/src/layout/UiHeader.vue b/web_src/src/layout/UiHeader.vue new file mode 100644 index 00000000..fa9be3ef --- /dev/null +++ b/web_src/src/layout/UiHeader.vue @@ -0,0 +1,166 @@ + + + + diff --git a/web_src/src/layout/index.vue b/web_src/src/layout/index.vue new file mode 100644 index 00000000..d6afcf64 --- /dev/null +++ b/web_src/src/layout/index.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/web_src/src/main.js b/web_src/src/main.js index ffd7fde5..e800718e 100644 --- a/web_src/src/main.js +++ b/web_src/src/main.js @@ -7,11 +7,13 @@ import router from './router/index.js'; import axios from 'axios'; import VueCookies from 'vue-cookies'; import echarts from 'echarts'; +import VCharts from 'v-charts'; import VueClipboard from 'vue-clipboard2'; import { Notification } from 'element-ui'; import Fingerprint2 from 'fingerprintjs2'; import VueClipboards from 'vue-clipboards'; +import Contextmenu from "vue-contextmenujs" // 生成唯一ID @@ -37,6 +39,8 @@ Vue.use(VueCookies); Vue.use(VueClipboards); Vue.prototype.$axios = axios; Vue.prototype.$notify = Notification; +Vue.use(Contextmenu); +Vue.use(VCharts); axios.defaults.baseURL = (process.env.NODE_ENV === 'development') ? process.env.BASE_API : ""; diff --git a/web_src/src/router/index.js b/web_src/src/router/index.js index 59bbb236..23c0a825 100644 --- a/web_src/src/router/index.js +++ b/web_src/src/router/index.js @@ -1,22 +1,26 @@ import Vue from 'vue' import VueRouter from 'vue-router' +import Layout from "../layout/index.vue" -import control from '../components/control.vue' +import console from '../components/console.vue' import deviceList from '../components/DeviceList.vue' import channelList from '../components/channelList.vue' +import gbRecordDetail from '../components/GBRecordDetail.vue' import pushVideoList from '../components/PushVideoList.vue' import streamProxyList from '../components/StreamProxyList.vue' -import devicePosition from '../components/devicePosition.vue' +import map from '../components/map.vue' import login from '../components/Login.vue' import parentPlatformList from '../components/ParentPlatformList.vue' import cloudRecord from '../components/CloudRecord.vue' import mediaServerManger from '../components/MediaServerManger.vue' -import test from '../components/test.vue' import web from '../components/setting/Web.vue' import sip from '../components/setting/Sip.vue' import media from '../components/setting/Media.vue' +import live from '../components/live.vue' +import deviceTree from '../components/common/DeviceTree.vue' +import userManager from '../components/UserManager.vue' -import wasmPlayer from '../components/dialog/jessibuca.vue' +import wasmPlayer from '../components/common/jessibuca.vue' import rtcPlayer from '../components/dialog/rtcPlayer.vue' const originalPush = VueRouter.prototype.push @@ -32,69 +36,96 @@ export default new VueRouter({ routes: [ { path: '/', - component: control, - }, - { - path: '/deviceList', - component: deviceList, - }, - { - path: '/pushVideoList', - component: pushVideoList, - }, - { - path: '/streamProxyList', - component: streamProxyList, + name: 'home', + component: Layout, + redirect: '/console', + children: [ + { + path: '/console', + component: console, + }, + { + path: '/live', + component: live, + }, + { + path: '/deviceList', + component: deviceList, + }, + { + path: '/pushVideoList', + component: pushVideoList, + }, + { + path: '/streamProxyList', + component: streamProxyList, + }, + { + path: '/channelList/:deviceId/:parentChannelId/', + name: 'channelList', + component: channelList, + }, + { + path: '/gbRecordDetail/:deviceId/:channelId/', + name: 'gbRecordDetail', + component: gbRecordDetail, + }, + { + path: '/parentPlatformList/:count/:page', + name: 'parentPlatformList', + component: parentPlatformList, + }, + { + path: '/map/:deviceId/:parentChannelId/:count/:page', + name: 'map', + component: map, + }, + { + path: '/cloudRecord', + name: 'cloudRecord', + component: cloudRecord, + }, + { + path: '/mediaServerManger', + name: 'mediaServerManger', + component: mediaServerManger, + }, + { + path: '/setting/web', + name: 'web', + component: web, + }, + { + path: '/setting/sip', + name: 'sip', + component: sip, + }, + { + path: '/setting/media', + name: 'media', + component: media, + }, + { + path: '/map', + name: 'map', + component: map, + }, + { + path: '/userManager', + name: 'userManager', + component: userManager, + } + ] }, { path: '/login', name: '登录', component: login, }, - { - path: '/channelList/:deviceId/:parentChannelId/:count/:page', - name: 'channelList', - component: channelList, - }, - { - path: '/parentPlatformList/:count/:page', - name: 'parentPlatformList', - component: parentPlatformList, - }, - { - path: '/devicePosition/:deviceId/:parentChannelId/:count/:page', - name: 'devicePosition', - component: devicePosition, - }, - { - path: '/cloudRecord', - name: 'cloudRecord', - component: cloudRecord, - }, - { - path: '/mediaServerManger', - name: 'mediaServerManger', - component: mediaServerManger, - }, - { - path: '/setting/web', - name: 'web', - component: web, - }, - { - path: '/setting/sip', - name: 'sip', - component: sip, - }, - { - path: '/setting/media', - name: 'media', - component: media, - }, { path: '/test', - name: 'test', - component: test, + name: 'deviceTree', + component: deviceTree, }, { path: '/play/wasm/:url', diff --git a/web_src/static/css/iconfont.css b/web_src/static/css/iconfont.css index 4c7b6082..4636a730 100644 --- a/web_src/static/css/iconfont.css +++ b/web_src/static/css/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 1291092 */ - src: url('iconfont.woff2?t=1637741914969') format('woff2'), - url('iconfont.woff?t=1637741914969') format('woff'), - url('iconfont.ttf?t=1637741914969') format('truetype'); + src: url('iconfont.woff2?t=1673251105600') format('woff2'), + url('iconfont.woff?t=1673251105600') format('woff'), + url('iconfont.ttf?t=1673251105600') format('truetype'); } .iconfont { @@ -13,6 +13,122 @@ -moz-osx-font-smoothing: grayscale; } +.icon-slider:before { + content: "\e7e0"; +} + +.icon-slider-right:before { + content: "\ea19"; +} + +.icon-list:before { + content: "\e7de"; +} + +.icon-tree:before { + content: "\e7df"; +} + +.icon-shipin:before { + content: "\e7db"; +} + +.icon-shipin1:before { + content: "\e7dc"; +} + +.icon-shipin2:before { + content: "\e7dd"; +} + +.icon-LC_icon_gps_fill:before { + content: "\e7da"; +} + +.icon-jiedianleizhukongzhongxin1:before { + content: "\e9d0"; +} + +.icon-jiedianleizhukongzhongxin2:before { + content: "\e9d1"; +} + +.icon-jiedianleilianjipingtai:before { + content: "\e9d3"; +} + +.icon-jiedianleiquyu:before { + content: "\e9d4"; +} + +.icon-shebeileigis:before { + content: "\e9ec"; +} + +.icon-shebeileibanqiu:before { + content: "\e9f5"; +} + +.icon-shebeileibanqiugis:before { + content: "\e9f6"; +} + +.icon-shebeileijiankongdian:before { + content: "\ea07"; +} + +.icon-shebeileiqiangjitongdao:before { + content: "\ea15"; +} + +.icon-shebeileiqiuji:before { + content: "\ea17"; +} + +.icon-shebeileiqiujigis:before { + content: "\ea18"; +} + +.icon-xitongxinxi:before { + content: "\e7d6"; +} + +.icon-gbaojings:before { + content: "\e7d7"; +} + +.icon-gjichus:before { + content: "\e7d8"; +} + +.icon-gxunjians:before { + content: "\e7d9"; +} + +.icon-ziyuan:before { + content: "\e7d5"; +} + +.icon-shexiangtou1:before { + content: "\e7d4"; +} + +.icon-wxbzhuye:before { + content: "\e7d1"; +} + +.icon-mulu:before { + content: "\e7d2"; +} + +.icon-zhibo:before { + content: "\e8c1"; +} + +.icon-shexiangtou:before { + content: "\e7d3"; +} + .icon-suoxiao:before { content: "\e79a"; } @@ -49,7 +165,7 @@ content: "\e7a2"; } -.icon-kuaijin:before { +.icon-houtui:before { content: "\e7a3"; } @@ -57,7 +173,7 @@ content: "\e7a4"; } -.icon-kuaitui:before { +.icon-kuaijin:before { content: "\e7a5"; } diff --git a/web_src/static/css/iconfont.woff2 b/web_src/static/css/iconfont.woff2 index dea67281..f1cb24cf 100644 Binary files a/web_src/static/css/iconfont.woff2 and b/web_src/static/css/iconfont.woff2 differ diff --git a/web_src/static/favicon.ico b/web_src/static/favicon.ico new file mode 100644 index 00000000..2d672689 Binary files /dev/null and b/web_src/static/favicon.ico differ diff --git a/web_src/static/file/推流通道导入.zip b/web_src/static/file/推流通道导入.zip new file mode 100644 index 00000000..495702f4 Binary files /dev/null and b/web_src/static/file/推流通道导入.zip differ diff --git a/web_src/static/images/arrow.png b/web_src/static/images/arrow.png new file mode 100644 index 00000000..4d8df462 Binary files /dev/null and b/web_src/static/images/arrow.png differ diff --git a/web_src/static/images/gis/camera-offline.png b/web_src/static/images/gis/camera-offline.png new file mode 100644 index 00000000..67eb0fc7 Binary files /dev/null and b/web_src/static/images/gis/camera-offline.png differ diff --git a/web_src/static/images/gis/camera.png b/web_src/static/images/gis/camera.png new file mode 100644 index 00000000..a93bd551 Binary files /dev/null and b/web_src/static/images/gis/camera.png differ diff --git a/web_src/static/images/gis/camera1-offline.png b/web_src/static/images/gis/camera1-offline.png new file mode 100644 index 00000000..597209bf Binary files /dev/null and b/web_src/static/images/gis/camera1-offline.png differ diff --git a/web_src/static/images/gis/camera1.png b/web_src/static/images/gis/camera1.png new file mode 100644 index 00000000..e5f2b5ff Binary files /dev/null and b/web_src/static/images/gis/camera1.png differ diff --git a/web_src/static/images/gis/camera2-offline.png b/web_src/static/images/gis/camera2-offline.png new file mode 100644 index 00000000..4ddae239 Binary files /dev/null and b/web_src/static/images/gis/camera2-offline.png differ diff --git a/web_src/static/images/gis/camera2.png b/web_src/static/images/gis/camera2.png new file mode 100644 index 00000000..073bceb4 Binary files /dev/null and b/web_src/static/images/gis/camera2.png differ diff --git a/web_src/static/images/gis/camera3-offline.png b/web_src/static/images/gis/camera3-offline.png new file mode 100644 index 00000000..f05c2a34 Binary files /dev/null and b/web_src/static/images/gis/camera3-offline.png differ diff --git a/web_src/static/images/gis/camera3.png b/web_src/static/images/gis/camera3.png new file mode 100644 index 00000000..b40f67be Binary files /dev/null and b/web_src/static/images/gis/camera3.png differ diff --git a/web_src/static/js/ZLMRTCClient.js b/web_src/static/js/ZLMRTCClient.js index 288028c0..a44a2ac1 100644 --- a/web_src/static/js/ZLMRTCClient.js +++ b/web_src/static/js/ZLMRTCClient.js @@ -6,11 +6,17 @@ var ZLMRTCClient = (function (exports) { WEBRTC_ICE_CANDIDATE_ERROR: 'WEBRTC_ICE_CANDIDATE_ERROR', WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED: 'WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED', WEBRTC_ON_REMOTE_STREAMS: 'WEBRTC_ON_REMOTE_STREAMS', - WEBRTC_ON_LOCAL_STREAM: 'WEBRTC_ON_LOCAL_STREAM' + WEBRTC_ON_LOCAL_STREAM: 'WEBRTC_ON_LOCAL_STREAM', + WEBRTC_ON_CONNECTION_STATE_CHANGE: 'WEBRTC_ON_CONNECTION_STATE_CHANGE', + WEBRTC_ON_DATA_CHANNEL_OPEN: 'WEBRTC_ON_DATA_CHANNEL_OPEN', + WEBRTC_ON_DATA_CHANNEL_CLOSE: 'WEBRTC_ON_DATA_CHANNEL_CLOSE', + WEBRTC_ON_DATA_CHANNEL_ERR: 'WEBRTC_ON_DATA_CHANNEL_ERR', + WEBRTC_ON_DATA_CHANNEL_MSG: 'WEBRTC_ON_DATA_CHANNEL_MSG', + CAPTURE_STREAM_FAILED: 'CAPTURE_STREAM_FAILED' }; const VERSION = '1.0.1'; - const BUILD_DATE = 'Mon Apr 05 2021 10:22:48 GMT+0800 (中国标准时间)'; + const BUILD_DATE = 'Thu Mar 24 2022 17:42:57 GMT+0800 (China Standard Time)'; // Copyright (C) <2018> Intel Corporation // @@ -7284,11 +7290,16 @@ var ZLMRTCClient = (function (exports) { debug: false, // if output debug log zlmsdpUrl: '', - simulecast: false, + simulcast: false, useCamera: true, audioEnable: true, videoEnable: true, - recvOnly: false + recvOnly: false, + resolution: { + w: 0, + h: 0 + }, + usedatachannel: false }; this.options = Object.assign({}, defaults, options); @@ -7299,7 +7310,12 @@ var ZLMRTCClient = (function (exports) { this.e = { onicecandidate: this._onIceCandidate.bind(this), ontrack: this._onTrack.bind(this), - onicecandidateerror: this._onIceCandidateError.bind(this) + onicecandidateerror: this._onIceCandidateError.bind(this), + onconnectionstatechange: this._onconnectionstatechange.bind(this), + ondatachannelopen: this._onDataChannelOpen.bind(this), + ondatachannelmsg: this._onDataChannelMsg.bind(this), + ondatachannelerr: this._onDataChannelErr.bind(this), + ondatachannelclose: this._onDataChannelClose.bind(this) }; this._remoteStream = null; this._localStream = null; @@ -7307,6 +7323,17 @@ var ZLMRTCClient = (function (exports) { this.pc.onicecandidate = this.e.onicecandidate; this.pc.onicecandidateerror = this.e.onicecandidateerror; this.pc.ontrack = this.e.ontrack; + this.pc.onconnectionstatechange = this.e.onconnectionstatechange; + this.datachannel = null; + + if (this.options.usedatachannel) { + this.datachannel = this.pc.createDataChannel('chat'); + this.datachannel.onclose = this.e.ondatachannelclose; + this.datachannel.onerror = this.e.ondatachannelerr; + this.datachannel.onmessage = this.e.ondatachannelmsg; + this.datachannel.onopen = this.e.ondatachannelopen; + } + if (!this.options.recvOnly && (this.options.audioEnable || this.options.videoEnable)) this.start();else this.receive(); } @@ -7337,7 +7364,7 @@ var ZLMRTCClient = (function (exports) { let ret = response.data; //JSON.parse(response.data); if (ret.code != 0) { - // mean failed for offer/anwser exchange + // mean failed for offer/anwser exchange this.dispatch(Events$1.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, ret); return; } @@ -7347,7 +7374,7 @@ var ZLMRTCClient = (function (exports) { anwser.type = 'answer'; log(this.TAG, 'answer:', ret.sdp); this.pc.setRemoteDescription(anwser).then(() => { - log(this.TAG, 'set remote success'); + log(this.TAG, 'set remote sucess'); }).catch(e => { error(this.TAG, e); }); @@ -7377,6 +7404,10 @@ var ZLMRTCClient = (function (exports) { } } + if (this.options.resolution.w != 0 && this.options.resolution.h != 0 && typeof videoConstraints == 'object') { + videoConstraints.resolution = new Resolution(this.options.resolution.w, this.options.resolution.h); + } + MediaStreamFactory.createMediaStream(new StreamConstraints(audioConstraints, videoConstraints)).then(stream => { this._localStream = stream; this.dispatch(Events$1.WEBRTC_ON_LOCAL_STREAM, stream); @@ -7389,33 +7420,40 @@ var ZLMRTCClient = (function (exports) { sendEncodings: [] }; - if (this.options.simulecast && stream.getVideoTracks().length > 0) { + if (this.options.simulcast && stream.getVideoTracks().length > 0) { VideoTransceiverInit.sendEncodings = [{ - rid: 'q', - active: true, - scaleResolutionDownBy: 4.0 - }, { rid: 'h', active: true, - scaleResolutionDownBy: 2.0 + maxBitrate: 1000000 }, { - rid: 'f', - active: true + rid: 'm', + active: true, + maxBitrate: 500000, + scaleResolutionDownBy: 2 + }, { + rid: 'l', + active: true, + maxBitrate: 200000, + scaleResolutionDownBy: 4 }]; } - if (stream.getAudioTracks().length > 0) { - this.pc.addTransceiver(stream.getAudioTracks()[0], AudioTransceiverInit); - } else { - AudioTransceiverInit.direction = 'recvonly'; - this.pc.addTransceiver('audio', AudioTransceiverInit); + if (this.options.audioEnable) { + if (stream.getAudioTracks().length > 0) { + this.pc.addTransceiver(stream.getAudioTracks()[0], AudioTransceiverInit); + } else { + AudioTransceiverInit.direction = 'recvonly'; + this.pc.addTransceiver('audio', AudioTransceiverInit); + } } - if (stream.getVideoTracks().length > 0) { - this.pc.addTransceiver(stream.getVideoTracks()[0], VideoTransceiverInit); - } else { - VideoTransceiverInit.direction = 'recvonly'; - this.pc.addTransceiver('video', VideoTransceiverInit); + if (this.options.videoEnable) { + if (stream.getVideoTracks().length > 0) { + this.pc.addTransceiver(stream.getVideoTracks()[0], VideoTransceiverInit); + } else { + VideoTransceiverInit.direction = 'recvonly'; + this.pc.addTransceiver('video', VideoTransceiverInit); + } } /* stream.getTracks().forEach((track,idx)=>{ @@ -7440,7 +7478,7 @@ var ZLMRTCClient = (function (exports) { let ret = response.data; //JSON.parse(response.data); if (ret.code != 0) { - // mean failed for offer/anwser exchange + // mean failed for offer/anwser exchange this.dispatch(Events$1.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, ret); return; } @@ -7450,7 +7488,7 @@ var ZLMRTCClient = (function (exports) { anwser.type = 'answer'; log(this.TAG, 'answer:', ret.sdp); this.pc.setRemoteDescription(anwser).then(() => { - log(this.TAG, 'set remote success'); + log(this.TAG, 'set remote sucess'); }).catch(e => { error(this.TAG, e); }); @@ -7460,7 +7498,7 @@ var ZLMRTCClient = (function (exports) { error(this.TAG, e); }); }).catch(e => { - error(this.TAG, e); + this.dispatch(Events$1.CAPTURE_STREAM_FAILED); //debug.error(this.TAG,e); }); //const offerOptions = {}; /* @@ -7495,7 +7533,48 @@ var ZLMRTCClient = (function (exports) { this.dispatch(Events$1.WEBRTC_ICE_CANDIDATE_ERROR, event); } + _onconnectionstatechange(event) { + this.dispatch(Events$1.WEBRTC_ON_CONNECTION_STATE_CHANGE, this.pc.connectionState); + } + + _onDataChannelOpen(event) { + log(this.TAG, 'ondatachannel open:', event); + this.dispatch(Events$1.WEBRTC_ON_DATA_CHANNEL_OPEN, event); + } + + _onDataChannelMsg(event) { + log(this.TAG, 'ondatachannel msg:', event); + this.dispatch(Events$1.WEBRTC_ON_DATA_CHANNEL_MSG, event); + } + + _onDataChannelErr(event) { + log(this.TAG, 'ondatachannel err:', event); + this.dispatch(Events$1.WEBRTC_ON_DATA_CHANNEL_ERR, event); + } + + _onDataChannelClose(event) { + log(this.TAG, 'ondatachannel close:', event); + this.dispatch(Events$1.WEBRTC_ON_DATA_CHANNEL_CLOSE, event); + } + + sendMsg(data) { + if (this.datachannel != null) { + this.datachannel.send(data); + } else { + error(this.TAG, 'data channel is null'); + } + } + + closeDataChannel() { + if (this.datachannel) { + this.datachannel.close(); + this.datachannel = null; + } + } + close() { + this.closeDataChannel(); + if (this.pc) { this.pc.close(); this.pc = null; @@ -7528,15 +7607,108 @@ var ZLMRTCClient = (function (exports) { } + const quickScan = [{ + 'label': '4K(UHD)', + 'width': 3840, + 'height': 2160 + }, { + 'label': '1080p(FHD)', + 'width': 1920, + 'height': 1080 + }, { + 'label': 'UXGA', + 'width': 1600, + 'height': 1200, + 'ratio': '4:3' + }, { + 'label': '720p(HD)', + 'width': 1280, + 'height': 720 + }, { + 'label': 'SVGA', + 'width': 800, + 'height': 600 + }, { + 'label': 'VGA', + 'width': 640, + 'height': 480 + }, { + 'label': '360p(nHD)', + 'width': 640, + 'height': 360 + }, { + 'label': 'CIF', + 'width': 352, + 'height': 288 + }, { + 'label': 'QVGA', + 'width': 320, + 'height': 240 + }, { + 'label': 'QCIF', + 'width': 176, + 'height': 144 + }, { + 'label': 'QQVGA', + 'width': 160, + 'height': 120 + }]; + function GetSupportCameraResolutions$1() { + return new Promise(function (resolve, reject) { + let resolutions = []; + let ok = 0; + let err = 0; + + for (let i = 0; i < quickScan.length; ++i) { + let videoConstraints = new VideoTrackConstraints(VideoSourceInfo.CAMERA); + videoConstraints.resolution = new Resolution(quickScan[i].width, quickScan[i].height); + MediaStreamFactory.createMediaStream(new StreamConstraints(false, videoConstraints)).then(stream => { + resolutions.push(quickScan[i]); + ok++; + + if (ok + err == quickScan.length) { + resolve(resolutions); + } + }).catch(e => { + err++; + + if (ok + err == quickScan.length) { + resolve(resolutions); + } + }); + } + }); + } + function GetAllScanResolution$1() { + return quickScan; + } + function isSupportResolution$1(w, h) { + return new Promise(function (resolve, reject) { + let videoConstraints = new VideoTrackConstraints(VideoSourceInfo.CAMERA); + videoConstraints.resolution = new Resolution(w, h); + MediaStreamFactory.createMediaStream(new StreamConstraints(false, videoConstraints)).then(stream => { + resolve(); + }).catch(e => { + reject(e); + }); + }); + } + console.log('build date:', BUILD_DATE); console.log('version:', VERSION); const Events = Events$1; const Media = media; const Endpoint = RTCEndpoint; + const GetSupportCameraResolutions = GetSupportCameraResolutions$1; + const GetAllScanResolution = GetAllScanResolution$1; + const isSupportResolution = isSupportResolution$1; exports.Endpoint = Endpoint; exports.Events = Events; + exports.GetAllScanResolution = GetAllScanResolution; + exports.GetSupportCameraResolutions = GetSupportCameraResolutions; exports.Media = Media; + exports.isSupportResolution = isSupportResolution; Object.defineProperty(exports, '__esModule', { value: true }); diff --git a/web_src/static/js/ZLMRTCClient.js.map b/web_src/static/js/ZLMRTCClient.js.map new file mode 100644 index 00000000..34c0b568 --- /dev/null +++ b/web_src/static/js/ZLMRTCClient.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ZLMRTCClient.js","sources":["../src/base/event.js","../src/ulity/version.js","../src/base/utils.js","../src/base/mediaformat.js","../node_modules/webrtc-adapter/src/js/utils.js","../node_modules/webrtc-adapter/src/js/chrome/getusermedia.js","../node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js","../node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js","../node_modules/webrtc-adapter/src/js/edge/filtericeservers.js","../node_modules/sdp/sdp.js","../node_modules/rtcpeerconnection-shim/rtcpeerconnection.js","../node_modules/webrtc-adapter/src/js/edge/getusermedia.js","../node_modules/webrtc-adapter/src/js/edge/getdisplaymedia.js","../node_modules/webrtc-adapter/src/js/edge/edge_shim.js","../node_modules/webrtc-adapter/src/js/firefox/getusermedia.js","../node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js","../node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js","../node_modules/webrtc-adapter/src/js/safari/safari_shim.js","../node_modules/webrtc-adapter/src/js/common_shim.js","../node_modules/webrtc-adapter/src/js/adapter_factory.js","../node_modules/webrtc-adapter/src/js/adapter_core.js","../src/base/mediastream-factory.js","../src/base/export.js","../src/ulity/debug.js","../src/ulity/event.js","../node_modules/axios/lib/helpers/bind.js","../node_modules/axios/lib/utils.js","../node_modules/axios/lib/helpers/buildURL.js","../node_modules/axios/lib/core/InterceptorManager.js","../node_modules/axios/lib/core/transformData.js","../node_modules/axios/lib/cancel/isCancel.js","../node_modules/axios/lib/helpers/normalizeHeaderName.js","../node_modules/axios/lib/core/enhanceError.js","../node_modules/axios/lib/core/createError.js","../node_modules/axios/lib/core/settle.js","../node_modules/axios/lib/helpers/cookies.js","../node_modules/axios/lib/helpers/isAbsoluteURL.js","../node_modules/axios/lib/helpers/combineURLs.js","../node_modules/axios/lib/core/buildFullPath.js","../node_modules/axios/lib/helpers/parseHeaders.js","../node_modules/axios/lib/helpers/isURLSameOrigin.js","../node_modules/axios/lib/adapters/xhr.js","../node_modules/axios/lib/defaults.js","../node_modules/axios/lib/core/dispatchRequest.js","../node_modules/axios/lib/core/mergeConfig.js","../node_modules/axios/lib/core/Axios.js","../node_modules/axios/lib/cancel/Cancel.js","../node_modules/axios/lib/cancel/CancelToken.js","../node_modules/axios/lib/helpers/spread.js","../node_modules/axios/lib/helpers/isAxiosError.js","../node_modules/axios/lib/axios.js","../node_modules/axios/index.js","../src/endpoint/endpoint.js","../src/base/resolutionfind.js","../src/export.js"],"sourcesContent":["const Events = {\n\tWEBRTC_NOT_SUPPORT : 'WEBRTC_NOT_SUPPORT',\n\tWEBRTC_ICE_CANDIDATE_ERROR : 'WEBRTC_ICE_CANDIDATE_ERROR',\n\tWEBRTC_OFFER_ANWSER_EXCHANGE_FAILED:'WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED',\n\tWEBRTC_ON_REMOTE_STREAMS:'WEBRTC_ON_REMOTE_STREAMS',\n\tWEBRTC_ON_LOCAL_STREAM:'WEBRTC_ON_LOCAL_STREAM',\n\tWEBRTC_ON_CONNECTION_STATE_CHANGE:'WEBRTC_ON_CONNECTION_STATE_CHANGE',\n\tWEBRTC_ON_DATA_CHANNEL_OPEN:'WEBRTC_ON_DATA_CHANNEL_OPEN',\n\tWEBRTC_ON_DATA_CHANNEL_CLOSE:'WEBRTC_ON_DATA_CHANNEL_CLOSE',\n\tWEBRTC_ON_DATA_CHANNEL_ERR:'WEBRTC_ON_DATA_CHANNEL_ERR',\n\tWEBRTC_ON_DATA_CHANNEL_MSG:'WEBRTC_ON_DATA_CHANNEL_MSG',\n\tCAPTURE_STREAM_FAILED:'CAPTURE_STREAM_FAILED',\n};\n\nexport default Events;","export const VERSION = '__VERSION__';\nexport const BUILD_DATE = '__BUILD_DATE__';","// Copyright (C) <2018> Intel Corporation\n//\n// SPDX-License-Identifier: Apache-2.0\n\n\n// eslint-disable-next-line require-jsdoc\nexport function isFirefox() {\n return window.navigator.userAgent.match('Firefox') !== null;\n}\n// eslint-disable-next-line require-jsdoc\nexport function isChrome() {\n return window.navigator.userAgent.match('Chrome') !== null;\n}\n// eslint-disable-next-line require-jsdoc\nexport function isSafari() {\n return /^((?!chrome|android).)*safari/i.test(window.navigator.userAgent);\n}\n// eslint-disable-next-line require-jsdoc\nexport function isEdge() {\n return window.navigator.userAgent.match(/Edge\\/(\\d+).(\\d+)$/) !== null;\n}\n// eslint-disable-next-line require-jsdoc\nexport function createUuid() {\n return 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}","// Copyright (C) <2018> Intel Corporation\n//\n// SPDX-License-Identifier: Apache-2.0\n\n'use strict';\n/**\n * @class AudioSourceInfo\n * @classDesc Source info about an audio track. Values: 'mic', 'screen-cast', 'file', 'mixed'.\n * @memberOf Owt.Base\n * @readonly\n * @enum {string}\n */\nexport const AudioSourceInfo = {\n MIC: 'mic',\n SCREENCAST: 'screen-cast',\n FILE: 'file',\n MIXED: 'mixed',\n};\n\n/**\n * @class VideoSourceInfo\n * @classDesc Source info about a video track. Values: 'camera', 'screen-cast', 'file', 'mixed'.\n * @memberOf Owt.Base\n * @readonly\n * @enum {string}\n */\nexport const VideoSourceInfo = {\n CAMERA: 'camera',\n SCREENCAST: 'screen-cast',\n FILE: 'file',\n MIXED: 'mixed',\n};\n\n/**\n * @class TrackKind\n * @classDesc Kind of a track. Values: 'audio' for audio track, 'video' for video track, 'av' for both audio and video tracks.\n * @memberOf Owt.Base\n * @readonly\n * @enum {string}\n */\nexport const TrackKind = {\n /**\n * Audio tracks.\n * @type string\n */\n AUDIO: 'audio',\n /**\n * Video tracks.\n * @type string\n */\n VIDEO: 'video',\n /**\n * Both audio and video tracks.\n * @type string\n */\n AUDIO_AND_VIDEO: 'av',\n};\n/**\n * @class Resolution\n * @memberOf Owt.Base\n * @classDesc The Resolution defines the size of a rectangle.\n * @constructor\n * @param {number} width\n * @param {number} height\n */\nexport class Resolution {\n // eslint-disable-next-line require-jsdoc\n constructor(width, height) {\n /**\n * @member {number} width\n * @instance\n * @memberof Owt.Base.Resolution\n */\n this.width = width;\n /**\n * @member {number} height\n * @instance\n * @memberof Owt.Base.Resolution\n */\n this.height = height;\n }\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n'use strict';\n\nlet logDisabled_ = true;\nlet deprecationWarnings_ = true;\n\n/**\n * Extract browser version out of the provided user agent string.\n *\n * @param {!string} uastring userAgent string.\n * @param {!string} expr Regular expression used as match criteria.\n * @param {!number} pos position in the version string to be returned.\n * @return {!number} browser version.\n */\nexport function extractVersion(uastring, expr, pos) {\n const match = uastring.match(expr);\n return match && match.length >= pos && parseInt(match[pos], 10);\n}\n\n// Wraps the peerconnection event eventNameToWrap in a function\n// which returns the modified event object (or false to prevent\n// the event).\nexport function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {\n if (!window.RTCPeerConnection) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n const nativeAddEventListener = proto.addEventListener;\n proto.addEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap) {\n return nativeAddEventListener.apply(this, arguments);\n }\n const wrappedCallback = (e) => {\n const modifiedEvent = wrapper(e);\n if (modifiedEvent) {\n if (cb.handleEvent) {\n cb.handleEvent(modifiedEvent);\n } else {\n cb(modifiedEvent);\n }\n }\n };\n this._eventMap = this._eventMap || {};\n if (!this._eventMap[eventNameToWrap]) {\n this._eventMap[eventNameToWrap] = new Map();\n }\n this._eventMap[eventNameToWrap].set(cb, wrappedCallback);\n return nativeAddEventListener.apply(this, [nativeEventName,\n wrappedCallback]);\n };\n\n const nativeRemoveEventListener = proto.removeEventListener;\n proto.removeEventListener = function(nativeEventName, cb) {\n if (nativeEventName !== eventNameToWrap || !this._eventMap\n || !this._eventMap[eventNameToWrap]) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n if (!this._eventMap[eventNameToWrap].has(cb)) {\n return nativeRemoveEventListener.apply(this, arguments);\n }\n const unwrappedCb = this._eventMap[eventNameToWrap].get(cb);\n this._eventMap[eventNameToWrap].delete(cb);\n if (this._eventMap[eventNameToWrap].size === 0) {\n delete this._eventMap[eventNameToWrap];\n }\n if (Object.keys(this._eventMap).length === 0) {\n delete this._eventMap;\n }\n return nativeRemoveEventListener.apply(this, [nativeEventName,\n unwrappedCb]);\n };\n\n Object.defineProperty(proto, 'on' + eventNameToWrap, {\n get() {\n return this['_on' + eventNameToWrap];\n },\n set(cb) {\n if (this['_on' + eventNameToWrap]) {\n this.removeEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap]);\n delete this['_on' + eventNameToWrap];\n }\n if (cb) {\n this.addEventListener(eventNameToWrap,\n this['_on' + eventNameToWrap] = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n}\n\nexport function disableLog(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n logDisabled_ = bool;\n return (bool) ? 'adapter.js logging disabled' :\n 'adapter.js logging enabled';\n}\n\n/**\n * Disable or enable deprecation warnings\n * @param {!boolean} bool set to true to disable warnings.\n */\nexport function disableWarnings(bool) {\n if (typeof bool !== 'boolean') {\n return new Error('Argument type: ' + typeof bool +\n '. Please use a boolean.');\n }\n deprecationWarnings_ = !bool;\n return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');\n}\n\nexport function log() {\n if (typeof window === 'object') {\n if (logDisabled_) {\n return;\n }\n if (typeof console !== 'undefined' && typeof console.log === 'function') {\n console.log.apply(console, arguments);\n }\n }\n}\n\n/**\n * Shows a deprecation warning suggesting the modern and spec-compatible API.\n */\nexport function deprecated(oldMethod, newMethod) {\n if (!deprecationWarnings_) {\n return;\n }\n console.warn(oldMethod + ' is deprecated, please use ' + newMethod +\n ' instead.');\n}\n\n/**\n * Browser detector.\n *\n * @return {object} result containing browser and version\n * properties.\n */\nexport function detectBrowser(window) {\n // Returned result object.\n const result = {browser: null, version: null};\n\n // Fail early if it's not a browser\n if (typeof window === 'undefined' || !window.navigator) {\n result.browser = 'Not a browser.';\n return result;\n }\n\n const {navigator} = window;\n\n if (navigator.mozGetUserMedia) { // Firefox.\n result.browser = 'firefox';\n result.version = extractVersion(navigator.userAgent,\n /Firefox\\/(\\d+)\\./, 1);\n } else if (navigator.webkitGetUserMedia ||\n (window.isSecureContext === false && window.webkitRTCPeerConnection &&\n !window.RTCIceGatherer)) {\n // Chrome, Chromium, Webview, Opera.\n // Version matches Chrome/WebRTC version.\n // Chrome 74 removed webkitGetUserMedia on http as well so we need the\n // more complicated fallback to webkitRTCPeerConnection.\n result.browser = 'chrome';\n result.version = extractVersion(navigator.userAgent,\n /Chrom(e|ium)\\/(\\d+)\\./, 2);\n } else if (navigator.mediaDevices &&\n navigator.userAgent.match(/Edge\\/(\\d+).(\\d+)$/)) { // Edge.\n result.browser = 'edge';\n result.version = extractVersion(navigator.userAgent,\n /Edge\\/(\\d+).(\\d+)$/, 2);\n } else if (window.RTCPeerConnection &&\n navigator.userAgent.match(/AppleWebKit\\/(\\d+)\\./)) { // Safari.\n result.browser = 'safari';\n result.version = extractVersion(navigator.userAgent,\n /AppleWebKit\\/(\\d+)\\./, 1);\n result.supportsUnifiedPlan = window.RTCRtpTransceiver &&\n 'currentDirection' in window.RTCRtpTransceiver.prototype;\n } else { // Default fallthrough: not supported.\n result.browser = 'Not a supported browser.';\n return result;\n }\n\n return result;\n}\n\n/**\n * Checks if something is an object.\n *\n * @param {*} val The something you want to check.\n * @return true if val is an object, false otherwise.\n */\nfunction isObject(val) {\n return Object.prototype.toString.call(val) === '[object Object]';\n}\n\n/**\n * Remove all empty objects and undefined values\n * from a nested object -- an enhanced and vanilla version\n * of Lodash's `compact`.\n */\nexport function compactObject(data) {\n if (!isObject(data)) {\n return data;\n }\n\n return Object.keys(data).reduce(function(accumulator, key) {\n const isObj = isObject(data[key]);\n const value = isObj ? compactObject(data[key]) : data[key];\n const isEmptyObject = isObj && !Object.keys(value).length;\n if (value === undefined || isEmptyObject) {\n return accumulator;\n }\n return Object.assign(accumulator, {[key]: value});\n }, {});\n}\n\n/* iterates the stats graph recursively. */\nexport function walkStats(stats, base, resultSet) {\n if (!base || resultSet.has(base.id)) {\n return;\n }\n resultSet.set(base.id, base);\n Object.keys(base).forEach(name => {\n if (name.endsWith('Id')) {\n walkStats(stats, stats.get(base[name]), resultSet);\n } else if (name.endsWith('Ids')) {\n base[name].forEach(id => {\n walkStats(stats, stats.get(id), resultSet);\n });\n }\n });\n}\n\n/* filter getStats for a sender/receiver track. */\nexport function filterStats(result, track, outbound) {\n const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';\n const filteredResult = new Map();\n if (track === null) {\n return filteredResult;\n }\n const trackStats = [];\n result.forEach(value => {\n if (value.type === 'track' &&\n value.trackIdentifier === track.id) {\n trackStats.push(value);\n }\n });\n trackStats.forEach(trackStat => {\n result.forEach(stats => {\n if (stats.type === streamStatsType && stats.trackId === trackStat.id) {\n walkStats(result, stats, filteredResult);\n }\n });\n });\n return filteredResult;\n}\n\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\nconst logging = utils.log;\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n\n if (!navigator.mediaDevices) {\n return;\n }\n\n const constraintsToChrome_ = function(c) {\n if (typeof c !== 'object' || c.mandatory || c.optional) {\n return c;\n }\n const cc = {};\n Object.keys(c).forEach(key => {\n if (key === 'require' || key === 'advanced' || key === 'mediaSource') {\n return;\n }\n const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};\n if (r.exact !== undefined && typeof r.exact === 'number') {\n r.min = r.max = r.exact;\n }\n const oldname_ = function(prefix, name) {\n if (prefix) {\n return prefix + name.charAt(0).toUpperCase() + name.slice(1);\n }\n return (name === 'deviceId') ? 'sourceId' : name;\n };\n if (r.ideal !== undefined) {\n cc.optional = cc.optional || [];\n let oc = {};\n if (typeof r.ideal === 'number') {\n oc[oldname_('min', key)] = r.ideal;\n cc.optional.push(oc);\n oc = {};\n oc[oldname_('max', key)] = r.ideal;\n cc.optional.push(oc);\n } else {\n oc[oldname_('', key)] = r.ideal;\n cc.optional.push(oc);\n }\n }\n if (r.exact !== undefined && typeof r.exact !== 'number') {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_('', key)] = r.exact;\n } else {\n ['min', 'max'].forEach(mix => {\n if (r[mix] !== undefined) {\n cc.mandatory = cc.mandatory || {};\n cc.mandatory[oldname_(mix, key)] = r[mix];\n }\n });\n }\n });\n if (c.advanced) {\n cc.optional = (cc.optional || []).concat(c.advanced);\n }\n return cc;\n };\n\n const shimConstraints_ = function(constraints, func) {\n if (browserDetails.version >= 61) {\n return func(constraints);\n }\n constraints = JSON.parse(JSON.stringify(constraints));\n if (constraints && typeof constraints.audio === 'object') {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n constraints = JSON.parse(JSON.stringify(constraints));\n remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');\n remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');\n constraints.audio = constraintsToChrome_(constraints.audio);\n }\n if (constraints && typeof constraints.video === 'object') {\n // Shim facingMode for mobile & surface pro.\n let face = constraints.video.facingMode;\n face = face && ((typeof face === 'object') ? face : {ideal: face});\n const getSupportedFacingModeLies = browserDetails.version < 66;\n\n if ((face && (face.exact === 'user' || face.exact === 'environment' ||\n face.ideal === 'user' || face.ideal === 'environment')) &&\n !(navigator.mediaDevices.getSupportedConstraints &&\n navigator.mediaDevices.getSupportedConstraints().facingMode &&\n !getSupportedFacingModeLies)) {\n delete constraints.video.facingMode;\n let matches;\n if (face.exact === 'environment' || face.ideal === 'environment') {\n matches = ['back', 'rear'];\n } else if (face.exact === 'user' || face.ideal === 'user') {\n matches = ['front'];\n }\n if (matches) {\n // Look for matches in label, or use last cam for back (typical).\n return navigator.mediaDevices.enumerateDevices()\n .then(devices => {\n devices = devices.filter(d => d.kind === 'videoinput');\n let dev = devices.find(d => matches.some(match =>\n d.label.toLowerCase().includes(match)));\n if (!dev && devices.length && matches.includes('back')) {\n dev = devices[devices.length - 1]; // more likely the back cam\n }\n if (dev) {\n constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :\n {ideal: dev.deviceId};\n }\n constraints.video = constraintsToChrome_(constraints.video);\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n });\n }\n }\n constraints.video = constraintsToChrome_(constraints.video);\n }\n logging('chrome: ' + JSON.stringify(constraints));\n return func(constraints);\n };\n\n const shimError_ = function(e) {\n if (browserDetails.version >= 64) {\n return e;\n }\n return {\n name: {\n PermissionDeniedError: 'NotAllowedError',\n PermissionDismissedError: 'NotAllowedError',\n InvalidStateError: 'NotAllowedError',\n DevicesNotFoundError: 'NotFoundError',\n ConstraintNotSatisfiedError: 'OverconstrainedError',\n TrackStartError: 'NotReadableError',\n MediaDeviceFailedDueToShutdown: 'NotAllowedError',\n MediaDeviceKillSwitchOn: 'NotAllowedError',\n TabCaptureError: 'AbortError',\n ScreenCaptureError: 'AbortError',\n DeviceCaptureError: 'AbortError'\n }[e.name] || e.name,\n message: e.message,\n constraint: e.constraint || e.constraintName,\n toString() {\n return this.name + (this.message && ': ') + this.message;\n }\n };\n };\n\n const getUserMedia_ = function(constraints, onSuccess, onError) {\n shimConstraints_(constraints, c => {\n navigator.webkitGetUserMedia(c, onSuccess, e => {\n if (onError) {\n onError(shimError_(e));\n }\n });\n });\n };\n navigator.getUserMedia = getUserMedia_.bind(navigator);\n\n // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia\n // function which returns a Promise, it does not accept spec-style\n // constraints.\n if (navigator.mediaDevices.getUserMedia) {\n const origGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(cs) {\n return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {\n if (c.audio && !stream.getAudioTracks().length ||\n c.video && !stream.getVideoTracks().length) {\n stream.getTracks().forEach(track => {\n track.stop();\n });\n throw new DOMException('', 'NotFoundError');\n }\n return stream;\n }, e => Promise.reject(shimError_(e))));\n };\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\nexport function shimGetDisplayMedia(window, getSourceId) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n // getSourceId is a function that returns a promise resolving with\n // the sourceId of the screen/window/tab to be shared.\n if (typeof getSourceId !== 'function') {\n console.error('shimGetDisplayMedia: getSourceId argument is not ' +\n 'a function');\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n return getSourceId(constraints)\n .then(sourceId => {\n const widthSpecified = constraints.video && constraints.video.width;\n const heightSpecified = constraints.video &&\n constraints.video.height;\n const frameRateSpecified = constraints.video &&\n constraints.video.frameRate;\n constraints.video = {\n mandatory: {\n chromeMediaSource: 'desktop',\n chromeMediaSourceId: sourceId,\n maxFrameRate: frameRateSpecified || 3\n }\n };\n if (widthSpecified) {\n constraints.video.mandatory.maxWidth = widthSpecified;\n }\n if (heightSpecified) {\n constraints.video.mandatory.maxHeight = heightSpecified;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n });\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\n\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimMediaStream(window) {\n window.MediaStream = window.MediaStream || window.webkitMediaStream;\n}\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in\n window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {\n get() {\n return this._ontrack;\n },\n set(f) {\n if (this._ontrack) {\n this.removeEventListener('track', this._ontrack);\n }\n this.addEventListener('track', this._ontrack = f);\n },\n enumerable: true,\n configurable: true\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n if (!this._ontrackpoly) {\n this._ontrackpoly = (e) => {\n // onaddstream does not fire when a track is added to an existing\n // stream. But stream.onaddtrack is implemented so we use that.\n e.stream.addEventListener('addtrack', te => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === te.track.id);\n } else {\n receiver = {track: te.track};\n }\n\n const event = new Event('track');\n event.track = te.track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n e.stream.getTracks().forEach(track => {\n let receiver;\n if (window.RTCPeerConnection.prototype.getReceivers) {\n receiver = this.getReceivers()\n .find(r => r.track && r.track.id === track.id);\n } else {\n receiver = {track};\n }\n const event = new Event('track');\n event.track = track;\n event.receiver = receiver;\n event.transceiver = {receiver};\n event.streams = [e.stream];\n this.dispatchEvent(event);\n });\n };\n this.addEventListener('addstream', this._ontrackpoly);\n }\n return origSetRemoteDescription.apply(this, arguments);\n };\n } else {\n // even if RTCRtpTransceiver is in window, it is only used and\n // emitted in unified-plan. Unfortunately this means we need\n // to unconditionally wrap the event.\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n if (!e.transceiver) {\n Object.defineProperty(e, 'transceiver',\n {value: {receiver: e.receiver}});\n }\n return e;\n });\n }\n}\n\nexport function shimGetSendersWithDtmf(window) {\n // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.\n if (typeof window === 'object' && window.RTCPeerConnection &&\n !('getSenders' in window.RTCPeerConnection.prototype) &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype) {\n const shimSenderWithDtmf = function(pc, track) {\n return {\n track,\n get dtmf() {\n if (this._dtmf === undefined) {\n if (track.kind === 'audio') {\n this._dtmf = pc.createDTMFSender(track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n },\n _pc: pc\n };\n };\n\n // augment addTrack when getSenders is not available.\n if (!window.RTCPeerConnection.prototype.getSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n this._senders = this._senders || [];\n return this._senders.slice(); // return a copy of the internal state.\n };\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n let sender = origAddTrack.apply(this, arguments);\n if (!sender) {\n sender = shimSenderWithDtmf(this, track);\n this._senders.push(sender);\n }\n return sender;\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n origRemoveTrack.apply(this, arguments);\n const idx = this._senders.indexOf(sender);\n if (idx !== -1) {\n this._senders.splice(idx, 1);\n }\n };\n }\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._senders = this._senders || [];\n origAddStream.apply(this, [stream]);\n stream.getTracks().forEach(track => {\n this._senders.push(shimSenderWithDtmf(this, track));\n });\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._senders = this._senders || [];\n origRemoveStream.apply(this, [stream]);\n\n stream.getTracks().forEach(track => {\n const sender = this._senders.find(s => s.track === track);\n if (sender) { // remove sender\n this._senders.splice(this._senders.indexOf(sender), 1);\n }\n });\n };\n } else if (typeof window === 'object' && window.RTCPeerConnection &&\n 'getSenders' in window.RTCPeerConnection.prototype &&\n 'createDTMFSender' in window.RTCPeerConnection.prototype &&\n window.RTCRtpSender &&\n !('dtmf' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n\n Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {\n get() {\n if (this._dtmf === undefined) {\n if (this.track.kind === 'audio') {\n this._dtmf = this._pc.createDTMFSender(this.track);\n } else {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n }\n });\n }\n}\n\nexport function shimGetStats(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n\n // If selector is a function then we are in the old style stats so just\n // pass back the original getStats format to avoid breaking old users.\n if (arguments.length > 0 && typeof selector === 'function') {\n return origGetStats.apply(this, arguments);\n }\n\n // When spec-style getStats is supported, return those when called with\n // either no arguments or the selector argument is null.\n if (origGetStats.length === 0 && (arguments.length === 0 ||\n typeof selector !== 'function')) {\n return origGetStats.apply(this, []);\n }\n\n const fixChromeStats_ = function(response) {\n const standardReport = {};\n const reports = response.result();\n reports.forEach(report => {\n const standardStats = {\n id: report.id,\n timestamp: report.timestamp,\n type: {\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n }[report.type] || report.type\n };\n report.names().forEach(name => {\n standardStats[name] = report.stat(name);\n });\n standardReport[standardStats.id] = standardStats;\n });\n\n return standardReport;\n };\n\n // shim getStats with maplike support\n const makeMapStats = function(stats) {\n return new Map(Object.keys(stats).map(key => [key, stats[key]]));\n };\n\n if (arguments.length >= 2) {\n const successCallbackWrapper_ = function(response) {\n onSucc(makeMapStats(fixChromeStats_(response)));\n };\n\n return origGetStats.apply(this, [successCallbackWrapper_,\n selector]);\n }\n\n // promise-support\n return new Promise((resolve, reject) => {\n origGetStats.apply(this, [\n function(response) {\n resolve(makeMapStats(fixChromeStats_(response)));\n }, reject]);\n }).then(onSucc, onErr);\n };\n}\n\nexport function shimSenderReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender && window.RTCRtpReceiver)) {\n return;\n }\n\n // shim sender stats.\n if (!('getStats' in window.RTCRtpSender.prototype)) {\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n const sender = this;\n return this._pc.getStats().then(result =>\n /* Note: this will include stats of all senders that\n * send a track with the same id as sender.track as\n * it is not possible to identify the RTCRtpSender.\n */\n utils.filterStats(result, sender.track, true));\n };\n }\n\n // shim receiver stats.\n if (!('getStats' in window.RTCRtpReceiver.prototype)) {\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers =\n function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n const receiver = this;\n return this._pc.getStats().then(result =>\n utils.filterStats(result, receiver.track, false));\n };\n }\n\n if (!('getStats' in window.RTCRtpSender.prototype &&\n 'getStats' in window.RTCRtpReceiver.prototype)) {\n return;\n }\n\n // shim RTCPeerConnection.getStats(track).\n const origGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n if (arguments.length > 0 &&\n arguments[0] instanceof window.MediaStreamTrack) {\n const track = arguments[0];\n let sender;\n let receiver;\n let err;\n this.getSenders().forEach(s => {\n if (s.track === track) {\n if (sender) {\n err = true;\n } else {\n sender = s;\n }\n }\n });\n this.getReceivers().forEach(r => {\n if (r.track === track) {\n if (receiver) {\n err = true;\n } else {\n receiver = r;\n }\n }\n return r.track === track;\n });\n if (err || (sender && receiver)) {\n return Promise.reject(new DOMException(\n 'There are more than one sender or receiver for the track.',\n 'InvalidAccessError'));\n } else if (sender) {\n return sender.getStats();\n } else if (receiver) {\n return receiver.getStats();\n }\n return Promise.reject(new DOMException(\n 'There is no sender or receiver for the track.',\n 'InvalidAccessError'));\n }\n return origGetStats.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrackWithNative(window) {\n // shim addTrack/removeTrack with native variants in order to make\n // the interactions with legacy getLocalStreams behave as in other browsers.\n // Keeps a mapping stream.id => [stream, rtpsenders...]\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n return Object.keys(this._shimmedLocalStreams)\n .map(streamId => this._shimmedLocalStreams[streamId][0]);\n };\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (!stream) {\n return origAddTrack.apply(this, arguments);\n }\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n const sender = origAddTrack.apply(this, arguments);\n if (!this._shimmedLocalStreams[stream.id]) {\n this._shimmedLocalStreams[stream.id] = [stream, sender];\n } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {\n this._shimmedLocalStreams[stream.id].push(sender);\n }\n return sender;\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n const existingSenders = this.getSenders();\n origAddStream.apply(this, arguments);\n const newSenders = this.getSenders()\n .filter(newSender => existingSenders.indexOf(newSender) === -1);\n this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n delete this._shimmedLocalStreams[stream.id];\n return origRemoveStream.apply(this, arguments);\n };\n\n const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n if (sender) {\n Object.keys(this._shimmedLocalStreams).forEach(streamId => {\n const idx = this._shimmedLocalStreams[streamId].indexOf(sender);\n if (idx !== -1) {\n this._shimmedLocalStreams[streamId].splice(idx, 1);\n }\n if (this._shimmedLocalStreams[streamId].length === 1) {\n delete this._shimmedLocalStreams[streamId];\n }\n });\n }\n return origRemoveTrack.apply(this, arguments);\n };\n}\n\nexport function shimAddTrackRemoveTrack(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // shim addTrack and removeTrack.\n if (window.RTCPeerConnection.prototype.addTrack &&\n browserDetails.version >= 65) {\n return shimAddTrackRemoveTrackWithNative(window);\n }\n\n // also shim pc.getLocalStreams when addTrack is shimmed\n // to return the original streams.\n const origGetLocalStreams = window.RTCPeerConnection.prototype\n .getLocalStreams;\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n const nativeStreams = origGetLocalStreams.apply(this);\n this._reverseStreams = this._reverseStreams || {};\n return nativeStreams.map(stream => this._reverseStreams[stream.id]);\n };\n\n const origAddStream = window.RTCPeerConnection.prototype.addStream;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n stream.getTracks().forEach(track => {\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n });\n // Add identity mapping for consistency with addTrack.\n // Unless this is being used with a stream from addTrack.\n if (!this._reverseStreams[stream.id]) {\n const newStream = new window.MediaStream(stream.getTracks());\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n stream = newStream;\n }\n origAddStream.apply(this, [stream]);\n };\n\n const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n\n origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]);\n delete this._reverseStreams[(this._streams[stream.id] ?\n this._streams[stream.id].id : stream.id)];\n delete this._streams[stream.id];\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, stream) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n const streams = [].slice.call(arguments, 1);\n if (streams.length !== 1 ||\n !streams[0].getTracks().find(t => t === track)) {\n // this is not fully correct but all we can manage without\n // [[associated MediaStreams]] internal slot.\n throw new DOMException(\n 'The adapter.js addTrack polyfill only supports a single ' +\n ' stream which is associated with the specified track.',\n 'NotSupportedError');\n }\n\n const alreadyExists = this.getSenders().find(s => s.track === track);\n if (alreadyExists) {\n throw new DOMException('Track already exists.',\n 'InvalidAccessError');\n }\n\n this._streams = this._streams || {};\n this._reverseStreams = this._reverseStreams || {};\n const oldStream = this._streams[stream.id];\n if (oldStream) {\n // this is using odd Chrome behaviour, use with caution:\n // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815\n // Note: we rely on the high-level addTrack/dtmf shim to\n // create the sender with a dtmf sender.\n oldStream.addTrack(track);\n\n // Trigger ONN async.\n Promise.resolve().then(() => {\n this.dispatchEvent(new Event('negotiationneeded'));\n });\n } else {\n const newStream = new window.MediaStream([track]);\n this._streams[stream.id] = newStream;\n this._reverseStreams[newStream.id] = stream;\n this.addStream(newStream);\n }\n return this.getSenders().find(s => s.track === track);\n };\n\n // replace the internal stream id with the external one and\n // vice versa.\n function replaceInternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(internalStream.id, 'g'),\n externalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n function replaceExternalStreamId(pc, description) {\n let sdp = description.sdp;\n Object.keys(pc._reverseStreams || []).forEach(internalId => {\n const externalStream = pc._reverseStreams[internalId];\n const internalStream = pc._streams[externalStream.id];\n sdp = sdp.replace(new RegExp(externalStream.id, 'g'),\n internalStream.id);\n });\n return new RTCSessionDescription({\n type: description.type,\n sdp\n });\n }\n ['createOffer', 'createAnswer'].forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n const args = arguments;\n const isLegacyCall = arguments.length &&\n typeof arguments[0] === 'function';\n if (isLegacyCall) {\n return nativeMethod.apply(this, [\n (description) => {\n const desc = replaceInternalStreamId(this, description);\n args[0].apply(null, [desc]);\n },\n (err) => {\n if (args[1]) {\n args[1].apply(null, err);\n }\n }, arguments[2]\n ]);\n }\n return nativeMethod.apply(this, arguments)\n .then(description => replaceInternalStreamId(this, description));\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n\n const origSetLocalDescription =\n window.RTCPeerConnection.prototype.setLocalDescription;\n window.RTCPeerConnection.prototype.setLocalDescription =\n function setLocalDescription() {\n if (!arguments.length || !arguments[0].type) {\n return origSetLocalDescription.apply(this, arguments);\n }\n arguments[0] = replaceExternalStreamId(this, arguments[0]);\n return origSetLocalDescription.apply(this, arguments);\n };\n\n // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier\n\n const origLocalDescription = Object.getOwnPropertyDescriptor(\n window.RTCPeerConnection.prototype, 'localDescription');\n Object.defineProperty(window.RTCPeerConnection.prototype,\n 'localDescription', {\n get() {\n const description = origLocalDescription.get.apply(this);\n if (description.type === '') {\n return description;\n }\n return replaceInternalStreamId(this, description);\n }\n });\n\n window.RTCPeerConnection.prototype.removeTrack =\n function removeTrack(sender) {\n if (this.signalingState === 'closed') {\n throw new DOMException(\n 'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n 'InvalidStateError');\n }\n // We can not yet check for sender instanceof RTCRtpSender\n // since we shim RTPSender. So we check if sender._pc is set.\n if (!sender._pc) {\n throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +\n 'does not implement interface RTCRtpSender.', 'TypeError');\n }\n const isLocal = sender._pc === this;\n if (!isLocal) {\n throw new DOMException('Sender was not created by this connection.',\n 'InvalidAccessError');\n }\n\n // Search for the native stream the senders track belongs to.\n this._streams = this._streams || {};\n let stream;\n Object.keys(this._streams).forEach(streamid => {\n const hasTrack = this._streams[streamid].getTracks()\n .find(track => sender.track === track);\n if (hasTrack) {\n stream = this._streams[streamid];\n }\n });\n\n if (stream) {\n if (stream.getTracks().length === 1) {\n // if this is the last track of the stream, remove the stream. This\n // takes care of any shimmed _senders.\n this.removeStream(this._reverseStreams[stream.id]);\n } else {\n // relying on the same odd chrome behaviour as above.\n stream.removeTrack(sender.track);\n }\n this.dispatchEvent(new Event('negotiationneeded'));\n }\n };\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.webkitRTCPeerConnection;\n }\n if (!window.RTCPeerConnection) {\n return;\n }\n\n // shim implicit creation of RTCSessionDescription/RTCIceCandidate\n if (browserDetails.version < 53) {\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n}\n\n// Attempt to fix ONN in plan-b mode.\nexport function fixNegotiationNeeded(window, browserDetails) {\n utils.wrapPeerConnectionEvent(window, 'negotiationneeded', e => {\n const pc = e.target;\n if (browserDetails.version < 72 || (pc.getConfiguration &&\n pc.getConfiguration().sdpSemantics === 'plan-b')) {\n if (pc.signalingState !== 'stable') {\n return;\n }\n }\n return e;\n });\n}\n","/*\n * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\n// Edge does not like\n// 1) stun: filtered after 14393 unless ?transport=udp is present\n// 2) turn: that does not have all of turn:host:port?transport=udp\n// 3) turn: with ipv6 addresses\n// 4) turn: occurring muliple times\nexport function filterIceServers(iceServers, edgeVersion) {\n let hasTurn = false;\n iceServers = JSON.parse(JSON.stringify(iceServers));\n return iceServers.filter(server => {\n if (server && (server.urls || server.url)) {\n let urls = server.urls || server.url;\n if (server.url && !server.urls) {\n utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');\n }\n const isString = typeof urls === 'string';\n if (isString) {\n urls = [urls];\n }\n urls = urls.filter(url => {\n // filter STUN unconditionally.\n if (url.indexOf('stun:') === 0) {\n return false;\n }\n\n const validTurn = url.startsWith('turn') &&\n !url.startsWith('turn:[') &&\n url.includes('transport=udp');\n if (validTurn && !hasTurn) {\n hasTurn = true;\n return true;\n }\n return validTurn && !hasTurn;\n });\n\n delete server.url;\n server.urls = isString ? urls[0] : urls;\n return !!urls.length;\n }\n });\n}\n","/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nvar SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n return Math.random().toString(36).substr(2, 10);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n return blob.trim().split('\\n').map(function(line) {\n return line.trim();\n });\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n var parts = blob.split('\\nm=');\n return parts.map(function(part, index) {\n return (index > 0 ? 'm=' + part : part).trim() + '\\r\\n';\n });\n};\n\n// returns the session description.\nSDPUtils.getDescription = function(blob) {\n var sections = SDPUtils.splitSections(blob);\n return sections && sections[0];\n};\n\n// returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n var sections = SDPUtils.splitSections(blob);\n sections.shift();\n return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n return SDPUtils.splitLines(blob).filter(function(line) {\n return line.indexOf(prefix) === 0;\n });\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\nSDPUtils.parseCandidate = function(line) {\n var parts;\n // Parse both variants.\n if (line.indexOf('a=candidate:') === 0) {\n parts = line.substring(12).split(' ');\n } else {\n parts = line.substring(10).split(' ');\n }\n\n var candidate = {\n foundation: parts[0],\n component: parseInt(parts[1], 10),\n protocol: parts[2].toLowerCase(),\n priority: parseInt(parts[3], 10),\n ip: parts[4],\n address: parts[4], // address is an alias for ip.\n port: parseInt(parts[5], 10),\n // skip parts[6] == 'typ'\n type: parts[7]\n };\n\n for (var i = 8; i < parts.length; i += 2) {\n switch (parts[i]) {\n case 'raddr':\n candidate.relatedAddress = parts[i + 1];\n break;\n case 'rport':\n candidate.relatedPort = parseInt(parts[i + 1], 10);\n break;\n case 'tcptype':\n candidate.tcpType = parts[i + 1];\n break;\n case 'ufrag':\n candidate.ufrag = parts[i + 1]; // for backward compability.\n candidate.usernameFragment = parts[i + 1];\n break;\n default: // extension handling, in particular ufrag\n candidate[parts[i]] = parts[i + 1];\n break;\n }\n }\n return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\nSDPUtils.writeCandidate = function(candidate) {\n var sdp = [];\n sdp.push(candidate.foundation);\n sdp.push(candidate.component);\n sdp.push(candidate.protocol.toUpperCase());\n sdp.push(candidate.priority);\n sdp.push(candidate.address || candidate.ip);\n sdp.push(candidate.port);\n\n var type = candidate.type;\n sdp.push('typ');\n sdp.push(type);\n if (type !== 'host' && candidate.relatedAddress &&\n candidate.relatedPort) {\n sdp.push('raddr');\n sdp.push(candidate.relatedAddress);\n sdp.push('rport');\n sdp.push(candidate.relatedPort);\n }\n if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n sdp.push('tcptype');\n sdp.push(candidate.tcpType);\n }\n if (candidate.usernameFragment || candidate.ufrag) {\n sdp.push('ufrag');\n sdp.push(candidate.usernameFragment || candidate.ufrag);\n }\n return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n return line.substr(14).split(' ');\n};\n\n// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n var parts = line.substr(9).split(' ');\n var parsed = {\n payloadType: parseInt(parts.shift(), 10) // was: id\n };\n\n parts = parts[0].split('/');\n\n parsed.name = parts[0];\n parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n // legacy alias, got renamed back to channels in ORTC.\n parsed.numChannels = parsed.channels;\n return parsed;\n};\n\n// Generate an a=rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n var pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n var channels = codec.channels || codec.numChannels || 1;\n return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses an a=extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n var parts = line.substr(9).split(' ');\n return {\n id: parseInt(parts[0], 10),\n direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n uri: parts[1]\n };\n};\n\n// Generates a=extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n ? '/' + headerExtension.direction\n : '') +\n ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses an ftmp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\nSDPUtils.parseFmtp = function(line) {\n var parsed = {};\n var kv;\n var parts = line.substr(line.indexOf(' ') + 1).split(';');\n for (var j = 0; j < parts.length; j++) {\n kv = parts[j].trim().split('=');\n parsed[kv[0].trim()] = kv[1];\n }\n return parsed;\n};\n\n// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n var line = '';\n var pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.parameters && Object.keys(codec.parameters).length) {\n var params = [];\n Object.keys(codec.parameters).forEach(function(param) {\n if (codec.parameters[param]) {\n params.push(param + '=' + codec.parameters[param]);\n } else {\n params.push(param);\n }\n });\n line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n }\n return line;\n};\n\n// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n var parts = line.substr(line.indexOf(' ') + 1).split(' ');\n return {\n type: parts.shift(),\n parameter: parts.join(' ')\n };\n};\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n var lines = '';\n var pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n // FIXME: special handling for trr-int?\n codec.rtcpFeedback.forEach(function(fb) {\n lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n '\\r\\n';\n });\n }\n return lines;\n};\n\n// Parses an RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n var sp = line.indexOf(' ');\n var parts = {\n ssrc: parseInt(line.substr(7, sp - 7), 10)\n };\n var colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substr(sp + 1, colon - sp - 1);\n parts.value = line.substr(colon + 1);\n } else {\n parts.attribute = line.substr(sp + 1);\n }\n return parts;\n};\n\nSDPUtils.parseSsrcGroup = function(line) {\n var parts = line.substr(13).split(' ');\n return {\n semantics: parts.shift(),\n ssrcs: parts.map(function(ssrc) {\n return parseInt(ssrc, 10);\n })\n };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n if (mid) {\n return mid.substr(6);\n }\n};\n\nSDPUtils.parseFingerprint = function(line) {\n var parts = line.substr(14).split(' ');\n return {\n algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n value: parts[1]\n };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=fingerprint:');\n // Note: a=setup line is ignored since we use the 'auto' role.\n // Note2: 'algorithm' is not case sensitive except in Edge.\n return {\n role: 'auto',\n fingerprints: lines.map(SDPUtils.parseFingerprint)\n };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n var sdp = 'a=setup:' + setupType + '\\r\\n';\n params.fingerprints.forEach(function(fp) {\n sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n });\n return sdp;\n};\n\n// Parses a=crypto lines into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n var parts = line.substr(9).split(' ');\n return {\n tag: parseInt(parts[0], 10),\n cryptoSuite: parts[1],\n keyParams: parts[2],\n sessionParams: parts.slice(3),\n };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n return 'a=crypto:' + parameters.tag + ' ' +\n parameters.cryptoSuite + ' ' +\n (typeof parameters.keyParams === 'object'\n ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n : parameters.keyParams) +\n (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n if (keyParams.indexOf('inline:') !== 0) {\n return null;\n }\n var parts = keyParams.substr(7).split('|');\n return {\n keyMethod: 'inline',\n keySalt: parts[0],\n lifeTime: parts[1],\n mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n return keyParams.keyMethod + ':'\n + keyParams.keySalt +\n (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n (keyParams.mkiValue && keyParams.mkiLength\n ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n : '');\n};\n\n// Extracts all SDES paramters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=crypto:');\n return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-ufrag:')[0];\n var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-pwd:')[0];\n if (!(ufrag && pwd)) {\n return null;\n }\n return {\n usernameFragment: ufrag.substr(12),\n password: pwd.substr(10),\n };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n return 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n 'a=ice-pwd:' + params.password + '\\r\\n';\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n var description = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: [],\n rtcp: []\n };\n var lines = SDPUtils.splitLines(mediaSection);\n var mline = lines[0].split(' ');\n for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n var pt = mline[i];\n var rtpmapline = SDPUtils.matchPrefix(\n mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n if (rtpmapline) {\n var codec = SDPUtils.parseRtpMap(rtpmapline);\n var fmtps = SDPUtils.matchPrefix(\n mediaSection, 'a=fmtp:' + pt + ' ');\n // Only the first a=fmtp: is considered.\n codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n codec.rtcpFeedback = SDPUtils.matchPrefix(\n mediaSection, 'a=rtcp-fb:' + pt + ' ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.push(codec);\n // parse FEC mechanisms from rtpmap lines.\n switch (codec.name.toUpperCase()) {\n case 'RED':\n case 'ULPFEC':\n description.fecMechanisms.push(codec.name.toUpperCase());\n break;\n default: // only RED and ULPFEC are recognized as FEC mechanisms.\n break;\n }\n }\n }\n SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {\n description.headerExtensions.push(SDPUtils.parseExtmap(line));\n });\n // FIXME: parse rtcp.\n return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n var sdp = '';\n\n // Build the mline.\n sdp += 'm=' + kind + ' ';\n sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n sdp += ' UDP/TLS/RTP/SAVPF ';\n sdp += caps.codecs.map(function(codec) {\n if (codec.preferredPayloadType !== undefined) {\n return codec.preferredPayloadType;\n }\n return codec.payloadType;\n }).join(' ') + '\\r\\n';\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n caps.codecs.forEach(function(codec) {\n sdp += SDPUtils.writeRtpMap(codec);\n sdp += SDPUtils.writeFmtp(codec);\n sdp += SDPUtils.writeRtcpFb(codec);\n });\n var maxptime = 0;\n caps.codecs.forEach(function(codec) {\n if (codec.maxptime > maxptime) {\n maxptime = codec.maxptime;\n }\n });\n if (maxptime > 0) {\n sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n }\n sdp += 'a=rtcp-mux\\r\\n';\n\n if (caps.headerExtensions) {\n caps.headerExtensions.forEach(function(extension) {\n sdp += SDPUtils.writeExtmap(extension);\n });\n }\n // FIXME: write fecMechanisms.\n return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n var encodingParameters = [];\n var description = SDPUtils.parseRtpParameters(mediaSection);\n var hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n // filter a=ssrc:... cname:, ignore PlanB-msid\n var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(function(line) {\n return SDPUtils.parseSsrcMedia(line);\n })\n .filter(function(parts) {\n return parts.attribute === 'cname';\n });\n var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n var secondarySsrc;\n\n var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n .map(function(line) {\n var parts = line.substr(17).split(' ');\n return parts.map(function(part) {\n return parseInt(part, 10);\n });\n });\n if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n secondarySsrc = flows[0][1];\n }\n\n description.codecs.forEach(function(codec) {\n if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n var encParam = {\n ssrc: primarySsrc,\n codecPayloadType: parseInt(codec.parameters.apt, 10)\n };\n if (primarySsrc && secondarySsrc) {\n encParam.rtx = {ssrc: secondarySsrc};\n }\n encodingParameters.push(encParam);\n if (hasRed) {\n encParam = JSON.parse(JSON.stringify(encParam));\n encParam.fec = {\n ssrc: primarySsrc,\n mechanism: hasUlpfec ? 'red+ulpfec' : 'red'\n };\n encodingParameters.push(encParam);\n }\n }\n });\n if (encodingParameters.length === 0 && primarySsrc) {\n encodingParameters.push({\n ssrc: primarySsrc\n });\n }\n\n // we support both b=AS and b=TIAS but interpret AS as TIAS.\n var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n if (bandwidth.length) {\n if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n bandwidth = parseInt(bandwidth[0].substr(7), 10);\n } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n // use formula from JSEP to convert b=AS to TIAS value.\n bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95\n - (50 * 40 * 8);\n } else {\n bandwidth = undefined;\n }\n encodingParameters.forEach(function(params) {\n params.maxBitrate = bandwidth;\n });\n }\n return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n var rtcpParameters = {};\n\n // Gets the first SSRC. Note tha with RTX there might be multiple\n // SSRCs.\n var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(function(line) {\n return SDPUtils.parseSsrcMedia(line);\n })\n .filter(function(obj) {\n return obj.attribute === 'cname';\n })[0];\n if (remoteSsrc) {\n rtcpParameters.cname = remoteSsrc.value;\n rtcpParameters.ssrc = remoteSsrc.ssrc;\n }\n\n // Edge uses the compound attribute instead of reducedSize\n // compound is !reducedSize\n var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n rtcpParameters.reducedSize = rsize.length > 0;\n rtcpParameters.compound = rsize.length === 0;\n\n // parses the rtcp-mux attrіbute.\n // Note that Edge does not support unmuxed RTCP.\n var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n rtcpParameters.mux = mux.length > 0;\n\n return rtcpParameters;\n};\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n var parts;\n var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n if (spec.length === 1) {\n parts = spec[0].substr(7).split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(function(line) {\n return SDPUtils.parseSsrcMedia(line);\n })\n .filter(function(msidParts) {\n return msidParts.attribute === 'msid';\n });\n if (planB.length > 0) {\n parts = planB[0].value.split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n var mline = SDPUtils.parseMLine(mediaSection);\n var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n var maxMessageSize;\n if (maxSizeLine.length > 0) {\n maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);\n }\n if (isNaN(maxMessageSize)) {\n maxMessageSize = 65536;\n }\n var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n if (sctpPort.length > 0) {\n return {\n port: parseInt(sctpPort[0].substr(12), 10),\n protocol: mline.fmt,\n maxMessageSize: maxMessageSize\n };\n }\n var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n if (sctpMapLines.length > 0) {\n var parts = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:')[0]\n .substr(10)\n .split(' ');\n return {\n port: parseInt(parts[0], 10),\n protocol: parts[1],\n maxMessageSize: maxMessageSize\n };\n }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n var output = [];\n if (media.protocol !== 'DTLS/SCTP') {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctp-port:' + sctp.port + '\\r\\n'\n ];\n } else {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n'\n ];\n }\n if (sctp.maxMessageSize !== undefined) {\n output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n }\n return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n return Math.random().toString().substr(2, 21);\n};\n\n// Write boilder plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n var sessionId;\n var version = sessVer !== undefined ? sessVer : 2;\n if (sessId) {\n sessionId = sessId;\n } else {\n sessionId = SDPUtils.generateSessionId();\n }\n var user = sessUser || 'thisisadapterortc';\n // FIXME: sess-id should be an NTP timestamp.\n return 'v=0\\r\\n' +\n 'o=' + user + ' ' + sessionId + ' ' + version +\n ' IN IP4 127.0.0.1\\r\\n' +\n 's=-\\r\\n' +\n 't=0 0\\r\\n';\n};\n\nSDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {\n var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);\n\n // Map ICE parameters (ufrag, pwd) to SDP.\n sdp += SDPUtils.writeIceParameters(\n transceiver.iceGatherer.getLocalParameters());\n\n // Map DTLS parameters to SDP.\n sdp += SDPUtils.writeDtlsParameters(\n transceiver.dtlsTransport.getLocalParameters(),\n type === 'offer' ? 'actpass' : 'active');\n\n sdp += 'a=mid:' + transceiver.mid + '\\r\\n';\n\n if (transceiver.direction) {\n sdp += 'a=' + transceiver.direction + '\\r\\n';\n } else if (transceiver.rtpSender && transceiver.rtpReceiver) {\n sdp += 'a=sendrecv\\r\\n';\n } else if (transceiver.rtpSender) {\n sdp += 'a=sendonly\\r\\n';\n } else if (transceiver.rtpReceiver) {\n sdp += 'a=recvonly\\r\\n';\n } else {\n sdp += 'a=inactive\\r\\n';\n }\n\n if (transceiver.rtpSender) {\n // spec.\n var msid = 'msid:' + stream.id + ' ' +\n transceiver.rtpSender.track.id + '\\r\\n';\n sdp += 'a=' + msid;\n\n // for Chrome.\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n ' ' + msid;\n if (transceiver.sendEncodingParameters[0].rtx) {\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n ' ' + msid;\n sdp += 'a=ssrc-group:FID ' +\n transceiver.sendEncodingParameters[0].ssrc + ' ' +\n transceiver.sendEncodingParameters[0].rtx.ssrc +\n '\\r\\n';\n }\n }\n // FIXME: this should be written by writeRtpDescription.\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n ' cname:' + SDPUtils.localCName + '\\r\\n';\n if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n ' cname:' + SDPUtils.localCName + '\\r\\n';\n }\n return sdp;\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n var lines = SDPUtils.splitLines(mediaSection);\n for (var i = 0; i < lines.length; i++) {\n switch (lines[i]) {\n case 'a=sendrecv':\n case 'a=sendonly':\n case 'a=recvonly':\n case 'a=inactive':\n return lines[i].substr(2);\n default:\n // FIXME: What should happen here?\n }\n }\n if (sessionpart) {\n return SDPUtils.getDirection(sessionpart);\n }\n return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n var lines = SDPUtils.splitLines(mediaSection);\n var mline = lines[0].split(' ');\n return mline[0].substr(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n var lines = SDPUtils.splitLines(mediaSection);\n var parts = lines[0].substr(2).split(' ');\n return {\n kind: parts[0],\n port: parseInt(parts[1], 10),\n protocol: parts[2],\n fmt: parts.slice(3).join(' ')\n };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n var parts = line.substr(2).split(' ');\n return {\n username: parts[0],\n sessionId: parts[1],\n sessionVersion: parseInt(parts[2], 10),\n netType: parts[3],\n addressType: parts[4],\n address: parts[5]\n };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n if (typeof blob !== 'string' || blob.length === 0) {\n return false;\n }\n var lines = SDPUtils.splitLines(blob);\n for (var i = 0; i < lines.length; i++) {\n if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n return false;\n }\n // TODO: check the modifier a bit more.\n }\n return true;\n};\n\n// Expose public methods.\nif (typeof module === 'object') {\n module.exports = SDPUtils;\n}\n","/*\n * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n'use strict';\n\nvar SDPUtils = require('sdp');\n\nfunction fixStatsType(stat) {\n return {\n inboundrtp: 'inbound-rtp',\n outboundrtp: 'outbound-rtp',\n candidatepair: 'candidate-pair',\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n }[stat.type] || stat.type;\n}\n\nfunction writeMediaSection(transceiver, caps, type, stream, dtlsRole) {\n var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);\n\n // Map ICE parameters (ufrag, pwd) to SDP.\n sdp += SDPUtils.writeIceParameters(\n transceiver.iceGatherer.getLocalParameters());\n\n // Map DTLS parameters to SDP.\n sdp += SDPUtils.writeDtlsParameters(\n transceiver.dtlsTransport.getLocalParameters(),\n type === 'offer' ? 'actpass' : dtlsRole || 'active');\n\n sdp += 'a=mid:' + transceiver.mid + '\\r\\n';\n\n if (transceiver.rtpSender && transceiver.rtpReceiver) {\n sdp += 'a=sendrecv\\r\\n';\n } else if (transceiver.rtpSender) {\n sdp += 'a=sendonly\\r\\n';\n } else if (transceiver.rtpReceiver) {\n sdp += 'a=recvonly\\r\\n';\n } else {\n sdp += 'a=inactive\\r\\n';\n }\n\n if (transceiver.rtpSender) {\n var trackId = transceiver.rtpSender._initialTrackId ||\n transceiver.rtpSender.track.id;\n transceiver.rtpSender._initialTrackId = trackId;\n // spec.\n var msid = 'msid:' + (stream ? stream.id : '-') + ' ' +\n trackId + '\\r\\n';\n sdp += 'a=' + msid;\n // for Chrome. Legacy should no longer be required.\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n ' ' + msid;\n\n // RTX\n if (transceiver.sendEncodingParameters[0].rtx) {\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n ' ' + msid;\n sdp += 'a=ssrc-group:FID ' +\n transceiver.sendEncodingParameters[0].ssrc + ' ' +\n transceiver.sendEncodingParameters[0].rtx.ssrc +\n '\\r\\n';\n }\n }\n // FIXME: this should be written by writeRtpDescription.\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n ' cname:' + SDPUtils.localCName + '\\r\\n';\n if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {\n sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n ' cname:' + SDPUtils.localCName + '\\r\\n';\n }\n return sdp;\n}\n\n// Edge does not like\n// 1) stun: filtered after 14393 unless ?transport=udp is present\n// 2) turn: that does not have all of turn:host:port?transport=udp\n// 3) turn: with ipv6 addresses\n// 4) turn: occurring muliple times\nfunction filterIceServers(iceServers, edgeVersion) {\n var hasTurn = false;\n iceServers = JSON.parse(JSON.stringify(iceServers));\n return iceServers.filter(function(server) {\n if (server && (server.urls || server.url)) {\n var urls = server.urls || server.url;\n if (server.url && !server.urls) {\n console.warn('RTCIceServer.url is deprecated! Use urls instead.');\n }\n var isString = typeof urls === 'string';\n if (isString) {\n urls = [urls];\n }\n urls = urls.filter(function(url) {\n var validTurn = url.indexOf('turn:') === 0 &&\n url.indexOf('transport=udp') !== -1 &&\n url.indexOf('turn:[') === -1 &&\n !hasTurn;\n\n if (validTurn) {\n hasTurn = true;\n return true;\n }\n return url.indexOf('stun:') === 0 && edgeVersion >= 14393 &&\n url.indexOf('?transport=udp') === -1;\n });\n\n delete server.url;\n server.urls = isString ? urls[0] : urls;\n return !!urls.length;\n }\n });\n}\n\n// Determines the intersection of local and remote capabilities.\nfunction getCommonCapabilities(localCapabilities, remoteCapabilities) {\n var commonCapabilities = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: []\n };\n\n var findCodecByPayloadType = function(pt, codecs) {\n pt = parseInt(pt, 10);\n for (var i = 0; i < codecs.length; i++) {\n if (codecs[i].payloadType === pt ||\n codecs[i].preferredPayloadType === pt) {\n return codecs[i];\n }\n }\n };\n\n var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {\n var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);\n var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);\n return lCodec && rCodec &&\n lCodec.name.toLowerCase() === rCodec.name.toLowerCase();\n };\n\n localCapabilities.codecs.forEach(function(lCodec) {\n for (var i = 0; i < remoteCapabilities.codecs.length; i++) {\n var rCodec = remoteCapabilities.codecs[i];\n if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&\n lCodec.clockRate === rCodec.clockRate) {\n if (lCodec.name.toLowerCase() === 'rtx' &&\n lCodec.parameters && rCodec.parameters.apt) {\n // for RTX we need to find the local rtx that has a apt\n // which points to the same local codec as the remote one.\n if (!rtxCapabilityMatches(lCodec, rCodec,\n localCapabilities.codecs, remoteCapabilities.codecs)) {\n continue;\n }\n }\n rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy\n // number of channels is the highest common number of channels\n rCodec.numChannels = Math.min(lCodec.numChannels,\n rCodec.numChannels);\n // push rCodec so we reply with offerer payload type\n commonCapabilities.codecs.push(rCodec);\n\n // determine common feedback mechanisms\n rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {\n for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {\n if (lCodec.rtcpFeedback[j].type === fb.type &&\n lCodec.rtcpFeedback[j].parameter === fb.parameter) {\n return true;\n }\n }\n return false;\n });\n // FIXME: also need to determine .parameters\n // see https://github.com/openpeer/ortc/issues/569\n break;\n }\n }\n });\n\n localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {\n for (var i = 0; i < remoteCapabilities.headerExtensions.length;\n i++) {\n var rHeaderExtension = remoteCapabilities.headerExtensions[i];\n if (lHeaderExtension.uri === rHeaderExtension.uri) {\n commonCapabilities.headerExtensions.push(rHeaderExtension);\n break;\n }\n }\n });\n\n // FIXME: fecMechanisms\n return commonCapabilities;\n}\n\n// is action=setLocalDescription with type allowed in signalingState\nfunction isActionAllowedInSignalingState(action, type, signalingState) {\n return {\n offer: {\n setLocalDescription: ['stable', 'have-local-offer'],\n setRemoteDescription: ['stable', 'have-remote-offer']\n },\n answer: {\n setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],\n setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']\n }\n }[type][action].indexOf(signalingState) !== -1;\n}\n\nfunction maybeAddCandidate(iceTransport, candidate) {\n // Edge's internal representation adds some fields therefore\n // not all fieldѕ are taken into account.\n var alreadyAdded = iceTransport.getRemoteCandidates()\n .find(function(remoteCandidate) {\n return candidate.foundation === remoteCandidate.foundation &&\n candidate.ip === remoteCandidate.ip &&\n candidate.port === remoteCandidate.port &&\n candidate.priority === remoteCandidate.priority &&\n candidate.protocol === remoteCandidate.protocol &&\n candidate.type === remoteCandidate.type;\n });\n if (!alreadyAdded) {\n iceTransport.addRemoteCandidate(candidate);\n }\n return !alreadyAdded;\n}\n\n\nfunction makeError(name, description) {\n var e = new Error(description);\n e.name = name;\n // legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names\n e.code = {\n NotSupportedError: 9,\n InvalidStateError: 11,\n InvalidAccessError: 15,\n TypeError: undefined,\n OperationError: undefined\n }[name];\n return e;\n}\n\nmodule.exports = function(window, edgeVersion) {\n // https://w3c.github.io/mediacapture-main/#mediastream\n // Helper function to add the track to the stream and\n // dispatch the event ourselves.\n function addTrackToStreamAndFireEvent(track, stream) {\n stream.addTrack(track);\n stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack',\n {track: track}));\n }\n\n function removeTrackFromStreamAndFireEvent(track, stream) {\n stream.removeTrack(track);\n stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack',\n {track: track}));\n }\n\n function fireAddTrack(pc, track, receiver, streams) {\n var trackEvent = new Event('track');\n trackEvent.track = track;\n trackEvent.receiver = receiver;\n trackEvent.transceiver = {receiver: receiver};\n trackEvent.streams = streams;\n window.setTimeout(function() {\n pc._dispatchEvent('track', trackEvent);\n });\n }\n\n var RTCPeerConnection = function(config) {\n var pc = this;\n\n var _eventTarget = document.createDocumentFragment();\n ['addEventListener', 'removeEventListener', 'dispatchEvent']\n .forEach(function(method) {\n pc[method] = _eventTarget[method].bind(_eventTarget);\n });\n\n this.canTrickleIceCandidates = null;\n\n this.needNegotiation = false;\n\n this.localStreams = [];\n this.remoteStreams = [];\n\n this._localDescription = null;\n this._remoteDescription = null;\n\n this.signalingState = 'stable';\n this.iceConnectionState = 'new';\n this.connectionState = 'new';\n this.iceGatheringState = 'new';\n\n config = JSON.parse(JSON.stringify(config || {}));\n\n this.usingBundle = config.bundlePolicy === 'max-bundle';\n if (config.rtcpMuxPolicy === 'negotiate') {\n throw(makeError('NotSupportedError',\n 'rtcpMuxPolicy \\'negotiate\\' is not supported'));\n } else if (!config.rtcpMuxPolicy) {\n config.rtcpMuxPolicy = 'require';\n }\n\n switch (config.iceTransportPolicy) {\n case 'all':\n case 'relay':\n break;\n default:\n config.iceTransportPolicy = 'all';\n break;\n }\n\n switch (config.bundlePolicy) {\n case 'balanced':\n case 'max-compat':\n case 'max-bundle':\n break;\n default:\n config.bundlePolicy = 'balanced';\n break;\n }\n\n config.iceServers = filterIceServers(config.iceServers || [], edgeVersion);\n\n this._iceGatherers = [];\n if (config.iceCandidatePoolSize) {\n for (var i = config.iceCandidatePoolSize; i > 0; i--) {\n this._iceGatherers.push(new window.RTCIceGatherer({\n iceServers: config.iceServers,\n gatherPolicy: config.iceTransportPolicy\n }));\n }\n } else {\n config.iceCandidatePoolSize = 0;\n }\n\n this._config = config;\n\n // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...\n // everything that is needed to describe a SDP m-line.\n this.transceivers = [];\n\n this._sdpSessionId = SDPUtils.generateSessionId();\n this._sdpSessionVersion = 0;\n\n this._dtlsRole = undefined; // role for a=setup to use in answers.\n\n this._isClosed = false;\n };\n\n Object.defineProperty(RTCPeerConnection.prototype, 'localDescription', {\n configurable: true,\n get: function() {\n return this._localDescription;\n }\n });\n Object.defineProperty(RTCPeerConnection.prototype, 'remoteDescription', {\n configurable: true,\n get: function() {\n return this._remoteDescription;\n }\n });\n\n // set up event handlers on prototype\n RTCPeerConnection.prototype.onicecandidate = null;\n RTCPeerConnection.prototype.onaddstream = null;\n RTCPeerConnection.prototype.ontrack = null;\n RTCPeerConnection.prototype.onremovestream = null;\n RTCPeerConnection.prototype.onsignalingstatechange = null;\n RTCPeerConnection.prototype.oniceconnectionstatechange = null;\n RTCPeerConnection.prototype.onconnectionstatechange = null;\n RTCPeerConnection.prototype.onicegatheringstatechange = null;\n RTCPeerConnection.prototype.onnegotiationneeded = null;\n RTCPeerConnection.prototype.ondatachannel = null;\n\n RTCPeerConnection.prototype._dispatchEvent = function(name, event) {\n if (this._isClosed) {\n return;\n }\n this.dispatchEvent(event);\n if (typeof this['on' + name] === 'function') {\n this['on' + name](event);\n }\n };\n\n RTCPeerConnection.prototype._emitGatheringStateChange = function() {\n var event = new Event('icegatheringstatechange');\n this._dispatchEvent('icegatheringstatechange', event);\n };\n\n RTCPeerConnection.prototype.getConfiguration = function() {\n return this._config;\n };\n\n RTCPeerConnection.prototype.getLocalStreams = function() {\n return this.localStreams;\n };\n\n RTCPeerConnection.prototype.getRemoteStreams = function() {\n return this.remoteStreams;\n };\n\n // internal helper to create a transceiver object.\n // (which is not yet the same as the WebRTC 1.0 transceiver)\n RTCPeerConnection.prototype._createTransceiver = function(kind, doNotAdd) {\n var hasBundleTransport = this.transceivers.length > 0;\n var transceiver = {\n track: null,\n iceGatherer: null,\n iceTransport: null,\n dtlsTransport: null,\n localCapabilities: null,\n remoteCapabilities: null,\n rtpSender: null,\n rtpReceiver: null,\n kind: kind,\n mid: null,\n sendEncodingParameters: null,\n recvEncodingParameters: null,\n stream: null,\n associatedRemoteMediaStreams: [],\n wantReceive: true\n };\n if (this.usingBundle && hasBundleTransport) {\n transceiver.iceTransport = this.transceivers[0].iceTransport;\n transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;\n } else {\n var transports = this._createIceAndDtlsTransports();\n transceiver.iceTransport = transports.iceTransport;\n transceiver.dtlsTransport = transports.dtlsTransport;\n }\n if (!doNotAdd) {\n this.transceivers.push(transceiver);\n }\n return transceiver;\n };\n\n RTCPeerConnection.prototype.addTrack = function(track, stream) {\n if (this._isClosed) {\n throw makeError('InvalidStateError',\n 'Attempted to call addTrack on a closed peerconnection.');\n }\n\n var alreadyExists = this.transceivers.find(function(s) {\n return s.track === track;\n });\n\n if (alreadyExists) {\n throw makeError('InvalidAccessError', 'Track already exists.');\n }\n\n var transceiver;\n for (var i = 0; i < this.transceivers.length; i++) {\n if (!this.transceivers[i].track &&\n this.transceivers[i].kind === track.kind) {\n transceiver = this.transceivers[i];\n }\n }\n if (!transceiver) {\n transceiver = this._createTransceiver(track.kind);\n }\n\n this._maybeFireNegotiationNeeded();\n\n if (this.localStreams.indexOf(stream) === -1) {\n this.localStreams.push(stream);\n }\n\n transceiver.track = track;\n transceiver.stream = stream;\n transceiver.rtpSender = new window.RTCRtpSender(track,\n transceiver.dtlsTransport);\n return transceiver.rtpSender;\n };\n\n RTCPeerConnection.prototype.addStream = function(stream) {\n var pc = this;\n if (edgeVersion >= 15025) {\n stream.getTracks().forEach(function(track) {\n pc.addTrack(track, stream);\n });\n } else {\n // Clone is necessary for local demos mostly, attaching directly\n // to two different senders does not work (build 10547).\n // Fixed in 15025 (or earlier)\n var clonedStream = stream.clone();\n stream.getTracks().forEach(function(track, idx) {\n var clonedTrack = clonedStream.getTracks()[idx];\n track.addEventListener('enabled', function(event) {\n clonedTrack.enabled = event.enabled;\n });\n });\n clonedStream.getTracks().forEach(function(track) {\n pc.addTrack(track, clonedStream);\n });\n }\n };\n\n RTCPeerConnection.prototype.removeTrack = function(sender) {\n if (this._isClosed) {\n throw makeError('InvalidStateError',\n 'Attempted to call removeTrack on a closed peerconnection.');\n }\n\n if (!(sender instanceof window.RTCRtpSender)) {\n throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' +\n 'does not implement interface RTCRtpSender.');\n }\n\n var transceiver = this.transceivers.find(function(t) {\n return t.rtpSender === sender;\n });\n\n if (!transceiver) {\n throw makeError('InvalidAccessError',\n 'Sender was not created by this connection.');\n }\n var stream = transceiver.stream;\n\n transceiver.rtpSender.stop();\n transceiver.rtpSender = null;\n transceiver.track = null;\n transceiver.stream = null;\n\n // remove the stream from the set of local streams\n var localStreams = this.transceivers.map(function(t) {\n return t.stream;\n });\n if (localStreams.indexOf(stream) === -1 &&\n this.localStreams.indexOf(stream) > -1) {\n this.localStreams.splice(this.localStreams.indexOf(stream), 1);\n }\n\n this._maybeFireNegotiationNeeded();\n };\n\n RTCPeerConnection.prototype.removeStream = function(stream) {\n var pc = this;\n stream.getTracks().forEach(function(track) {\n var sender = pc.getSenders().find(function(s) {\n return s.track === track;\n });\n if (sender) {\n pc.removeTrack(sender);\n }\n });\n };\n\n RTCPeerConnection.prototype.getSenders = function() {\n return this.transceivers.filter(function(transceiver) {\n return !!transceiver.rtpSender;\n })\n .map(function(transceiver) {\n return transceiver.rtpSender;\n });\n };\n\n RTCPeerConnection.prototype.getReceivers = function() {\n return this.transceivers.filter(function(transceiver) {\n return !!transceiver.rtpReceiver;\n })\n .map(function(transceiver) {\n return transceiver.rtpReceiver;\n });\n };\n\n\n RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex,\n usingBundle) {\n var pc = this;\n if (usingBundle && sdpMLineIndex > 0) {\n return this.transceivers[0].iceGatherer;\n } else if (this._iceGatherers.length) {\n return this._iceGatherers.shift();\n }\n var iceGatherer = new window.RTCIceGatherer({\n iceServers: this._config.iceServers,\n gatherPolicy: this._config.iceTransportPolicy\n });\n Object.defineProperty(iceGatherer, 'state',\n {value: 'new', writable: true}\n );\n\n this.transceivers[sdpMLineIndex].bufferedCandidateEvents = [];\n this.transceivers[sdpMLineIndex].bufferCandidates = function(event) {\n var end = !event.candidate || Object.keys(event.candidate).length === 0;\n // polyfill since RTCIceGatherer.state is not implemented in\n // Edge 10547 yet.\n iceGatherer.state = end ? 'completed' : 'gathering';\n if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) {\n pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event);\n }\n };\n iceGatherer.addEventListener('localcandidate',\n this.transceivers[sdpMLineIndex].bufferCandidates);\n return iceGatherer;\n };\n\n // start gathering from an RTCIceGatherer.\n RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) {\n var pc = this;\n var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;\n if (iceGatherer.onlocalcandidate) {\n return;\n }\n var bufferedCandidateEvents =\n this.transceivers[sdpMLineIndex].bufferedCandidateEvents;\n this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null;\n iceGatherer.removeEventListener('localcandidate',\n this.transceivers[sdpMLineIndex].bufferCandidates);\n iceGatherer.onlocalcandidate = function(evt) {\n if (pc.usingBundle && sdpMLineIndex > 0) {\n // if we know that we use bundle we can drop candidates with\n // ѕdpMLineIndex > 0. If we don't do this then our state gets\n // confused since we dispose the extra ice gatherer.\n return;\n }\n var event = new Event('icecandidate');\n event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};\n\n var cand = evt.candidate;\n // Edge emits an empty object for RTCIceCandidateComplete‥\n var end = !cand || Object.keys(cand).length === 0;\n if (end) {\n // polyfill since RTCIceGatherer.state is not implemented in\n // Edge 10547 yet.\n if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') {\n iceGatherer.state = 'completed';\n }\n } else {\n if (iceGatherer.state === 'new') {\n iceGatherer.state = 'gathering';\n }\n // RTCIceCandidate doesn't have a component, needs to be added\n cand.component = 1;\n // also the usernameFragment. TODO: update SDP to take both variants.\n cand.ufrag = iceGatherer.getLocalParameters().usernameFragment;\n\n var serializedCandidate = SDPUtils.writeCandidate(cand);\n event.candidate = Object.assign(event.candidate,\n SDPUtils.parseCandidate(serializedCandidate));\n\n event.candidate.candidate = serializedCandidate;\n event.candidate.toJSON = function() {\n return {\n candidate: event.candidate.candidate,\n sdpMid: event.candidate.sdpMid,\n sdpMLineIndex: event.candidate.sdpMLineIndex,\n usernameFragment: event.candidate.usernameFragment\n };\n };\n }\n\n // update local description.\n var sections = SDPUtils.getMediaSections(pc._localDescription.sdp);\n if (!end) {\n sections[event.candidate.sdpMLineIndex] +=\n 'a=' + event.candidate.candidate + '\\r\\n';\n } else {\n sections[event.candidate.sdpMLineIndex] +=\n 'a=end-of-candidates\\r\\n';\n }\n pc._localDescription.sdp =\n SDPUtils.getDescription(pc._localDescription.sdp) +\n sections.join('');\n var complete = pc.transceivers.every(function(transceiver) {\n return transceiver.iceGatherer &&\n transceiver.iceGatherer.state === 'completed';\n });\n\n if (pc.iceGatheringState !== 'gathering') {\n pc.iceGatheringState = 'gathering';\n pc._emitGatheringStateChange();\n }\n\n // Emit candidate. Also emit null candidate when all gatherers are\n // complete.\n if (!end) {\n pc._dispatchEvent('icecandidate', event);\n }\n if (complete) {\n pc._dispatchEvent('icecandidate', new Event('icecandidate'));\n pc.iceGatheringState = 'complete';\n pc._emitGatheringStateChange();\n }\n };\n\n // emit already gathered candidates.\n window.setTimeout(function() {\n bufferedCandidateEvents.forEach(function(e) {\n iceGatherer.onlocalcandidate(e);\n });\n }, 0);\n };\n\n // Create ICE transport and DTLS transport.\n RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {\n var pc = this;\n var iceTransport = new window.RTCIceTransport(null);\n iceTransport.onicestatechange = function() {\n pc._updateIceConnectionState();\n pc._updateConnectionState();\n };\n\n var dtlsTransport = new window.RTCDtlsTransport(iceTransport);\n dtlsTransport.ondtlsstatechange = function() {\n pc._updateConnectionState();\n };\n dtlsTransport.onerror = function() {\n // onerror does not set state to failed by itself.\n Object.defineProperty(dtlsTransport, 'state',\n {value: 'failed', writable: true});\n pc._updateConnectionState();\n };\n\n return {\n iceTransport: iceTransport,\n dtlsTransport: dtlsTransport\n };\n };\n\n // Destroy ICE gatherer, ICE transport and DTLS transport.\n // Without triggering the callbacks.\n RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(\n sdpMLineIndex) {\n var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;\n if (iceGatherer) {\n delete iceGatherer.onlocalcandidate;\n delete this.transceivers[sdpMLineIndex].iceGatherer;\n }\n var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;\n if (iceTransport) {\n delete iceTransport.onicestatechange;\n delete this.transceivers[sdpMLineIndex].iceTransport;\n }\n var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;\n if (dtlsTransport) {\n delete dtlsTransport.ondtlsstatechange;\n delete dtlsTransport.onerror;\n delete this.transceivers[sdpMLineIndex].dtlsTransport;\n }\n };\n\n // Start the RTP Sender and Receiver for a transceiver.\n RTCPeerConnection.prototype._transceive = function(transceiver,\n send, recv) {\n var params = getCommonCapabilities(transceiver.localCapabilities,\n transceiver.remoteCapabilities);\n if (send && transceiver.rtpSender) {\n params.encodings = transceiver.sendEncodingParameters;\n params.rtcp = {\n cname: SDPUtils.localCName,\n compound: transceiver.rtcpParameters.compound\n };\n if (transceiver.recvEncodingParameters.length) {\n params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;\n }\n transceiver.rtpSender.send(params);\n }\n if (recv && transceiver.rtpReceiver && params.codecs.length > 0) {\n // remove RTX field in Edge 14942\n if (transceiver.kind === 'video'\n && transceiver.recvEncodingParameters\n && edgeVersion < 15019) {\n transceiver.recvEncodingParameters.forEach(function(p) {\n delete p.rtx;\n });\n }\n if (transceiver.recvEncodingParameters.length) {\n params.encodings = transceiver.recvEncodingParameters;\n } else {\n params.encodings = [{}];\n }\n params.rtcp = {\n compound: transceiver.rtcpParameters.compound\n };\n if (transceiver.rtcpParameters.cname) {\n params.rtcp.cname = transceiver.rtcpParameters.cname;\n }\n if (transceiver.sendEncodingParameters.length) {\n params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;\n }\n transceiver.rtpReceiver.receive(params);\n }\n };\n\n RTCPeerConnection.prototype.setLocalDescription = function(description) {\n var pc = this;\n\n // Note: pranswer is not supported.\n if (['offer', 'answer'].indexOf(description.type) === -1) {\n return Promise.reject(makeError('TypeError',\n 'Unsupported type \"' + description.type + '\"'));\n }\n\n if (!isActionAllowedInSignalingState('setLocalDescription',\n description.type, pc.signalingState) || pc._isClosed) {\n return Promise.reject(makeError('InvalidStateError',\n 'Can not set local ' + description.type +\n ' in state ' + pc.signalingState));\n }\n\n var sections;\n var sessionpart;\n if (description.type === 'offer') {\n // VERY limited support for SDP munging. Limited to:\n // * changing the order of codecs\n sections = SDPUtils.splitSections(description.sdp);\n sessionpart = sections.shift();\n sections.forEach(function(mediaSection, sdpMLineIndex) {\n var caps = SDPUtils.parseRtpParameters(mediaSection);\n pc.transceivers[sdpMLineIndex].localCapabilities = caps;\n });\n\n pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {\n pc._gather(transceiver.mid, sdpMLineIndex);\n });\n } else if (description.type === 'answer') {\n sections = SDPUtils.splitSections(pc._remoteDescription.sdp);\n sessionpart = sections.shift();\n var isIceLite = SDPUtils.matchPrefix(sessionpart,\n 'a=ice-lite').length > 0;\n sections.forEach(function(mediaSection, sdpMLineIndex) {\n var transceiver = pc.transceivers[sdpMLineIndex];\n var iceGatherer = transceiver.iceGatherer;\n var iceTransport = transceiver.iceTransport;\n var dtlsTransport = transceiver.dtlsTransport;\n var localCapabilities = transceiver.localCapabilities;\n var remoteCapabilities = transceiver.remoteCapabilities;\n\n // treat bundle-only as not-rejected.\n var rejected = SDPUtils.isRejected(mediaSection) &&\n SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;\n\n if (!rejected && !transceiver.rejected) {\n var remoteIceParameters = SDPUtils.getIceParameters(\n mediaSection, sessionpart);\n var remoteDtlsParameters = SDPUtils.getDtlsParameters(\n mediaSection, sessionpart);\n if (isIceLite) {\n remoteDtlsParameters.role = 'server';\n }\n\n if (!pc.usingBundle || sdpMLineIndex === 0) {\n pc._gather(transceiver.mid, sdpMLineIndex);\n if (iceTransport.state === 'new') {\n iceTransport.start(iceGatherer, remoteIceParameters,\n isIceLite ? 'controlling' : 'controlled');\n }\n if (dtlsTransport.state === 'new') {\n dtlsTransport.start(remoteDtlsParameters);\n }\n }\n\n // Calculate intersection of capabilities.\n var params = getCommonCapabilities(localCapabilities,\n remoteCapabilities);\n\n // Start the RTCRtpSender. The RTCRtpReceiver for this\n // transceiver has already been started in setRemoteDescription.\n pc._transceive(transceiver,\n params.codecs.length > 0,\n false);\n }\n });\n }\n\n pc._localDescription = {\n type: description.type,\n sdp: description.sdp\n };\n if (description.type === 'offer') {\n pc._updateSignalingState('have-local-offer');\n } else {\n pc._updateSignalingState('stable');\n }\n\n return Promise.resolve();\n };\n\n RTCPeerConnection.prototype.setRemoteDescription = function(description) {\n var pc = this;\n\n // Note: pranswer is not supported.\n if (['offer', 'answer'].indexOf(description.type) === -1) {\n return Promise.reject(makeError('TypeError',\n 'Unsupported type \"' + description.type + '\"'));\n }\n\n if (!isActionAllowedInSignalingState('setRemoteDescription',\n description.type, pc.signalingState) || pc._isClosed) {\n return Promise.reject(makeError('InvalidStateError',\n 'Can not set remote ' + description.type +\n ' in state ' + pc.signalingState));\n }\n\n var streams = {};\n pc.remoteStreams.forEach(function(stream) {\n streams[stream.id] = stream;\n });\n var receiverList = [];\n var sections = SDPUtils.splitSections(description.sdp);\n var sessionpart = sections.shift();\n var isIceLite = SDPUtils.matchPrefix(sessionpart,\n 'a=ice-lite').length > 0;\n var usingBundle = SDPUtils.matchPrefix(sessionpart,\n 'a=group:BUNDLE ').length > 0;\n pc.usingBundle = usingBundle;\n var iceOptions = SDPUtils.matchPrefix(sessionpart,\n 'a=ice-options:')[0];\n if (iceOptions) {\n pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ')\n .indexOf('trickle') >= 0;\n } else {\n pc.canTrickleIceCandidates = false;\n }\n\n sections.forEach(function(mediaSection, sdpMLineIndex) {\n var lines = SDPUtils.splitLines(mediaSection);\n var kind = SDPUtils.getKind(mediaSection);\n // treat bundle-only as not-rejected.\n var rejected = SDPUtils.isRejected(mediaSection) &&\n SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;\n var protocol = lines[0].substr(2).split(' ')[2];\n\n var direction = SDPUtils.getDirection(mediaSection, sessionpart);\n var remoteMsid = SDPUtils.parseMsid(mediaSection);\n\n var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier();\n\n // Reject datachannels which are not implemented yet.\n if (rejected || (kind === 'application' && (protocol === 'DTLS/SCTP' ||\n protocol === 'UDP/DTLS/SCTP'))) {\n // TODO: this is dangerous in the case where a non-rejected m-line\n // becomes rejected.\n pc.transceivers[sdpMLineIndex] = {\n mid: mid,\n kind: kind,\n protocol: protocol,\n rejected: true\n };\n return;\n }\n\n if (!rejected && pc.transceivers[sdpMLineIndex] &&\n pc.transceivers[sdpMLineIndex].rejected) {\n // recycle a rejected transceiver.\n pc.transceivers[sdpMLineIndex] = pc._createTransceiver(kind, true);\n }\n\n var transceiver;\n var iceGatherer;\n var iceTransport;\n var dtlsTransport;\n var rtpReceiver;\n var sendEncodingParameters;\n var recvEncodingParameters;\n var localCapabilities;\n\n var track;\n // FIXME: ensure the mediaSection has rtcp-mux set.\n var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);\n var remoteIceParameters;\n var remoteDtlsParameters;\n if (!rejected) {\n remoteIceParameters = SDPUtils.getIceParameters(mediaSection,\n sessionpart);\n remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,\n sessionpart);\n remoteDtlsParameters.role = 'client';\n }\n recvEncodingParameters =\n SDPUtils.parseRtpEncodingParameters(mediaSection);\n\n var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);\n\n var isComplete = SDPUtils.matchPrefix(mediaSection,\n 'a=end-of-candidates', sessionpart).length > 0;\n var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n .map(function(cand) {\n return SDPUtils.parseCandidate(cand);\n })\n .filter(function(cand) {\n return cand.component === 1;\n });\n\n // Check if we can use BUNDLE and dispose transports.\n if ((description.type === 'offer' || description.type === 'answer') &&\n !rejected && usingBundle && sdpMLineIndex > 0 &&\n pc.transceivers[sdpMLineIndex]) {\n pc._disposeIceAndDtlsTransports(sdpMLineIndex);\n pc.transceivers[sdpMLineIndex].iceGatherer =\n pc.transceivers[0].iceGatherer;\n pc.transceivers[sdpMLineIndex].iceTransport =\n pc.transceivers[0].iceTransport;\n pc.transceivers[sdpMLineIndex].dtlsTransport =\n pc.transceivers[0].dtlsTransport;\n if (pc.transceivers[sdpMLineIndex].rtpSender) {\n pc.transceivers[sdpMLineIndex].rtpSender.setTransport(\n pc.transceivers[0].dtlsTransport);\n }\n if (pc.transceivers[sdpMLineIndex].rtpReceiver) {\n pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport(\n pc.transceivers[0].dtlsTransport);\n }\n }\n if (description.type === 'offer' && !rejected) {\n transceiver = pc.transceivers[sdpMLineIndex] ||\n pc._createTransceiver(kind);\n transceiver.mid = mid;\n\n if (!transceiver.iceGatherer) {\n transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,\n usingBundle);\n }\n\n if (cands.length && transceiver.iceTransport.state === 'new') {\n if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {\n transceiver.iceTransport.setRemoteCandidates(cands);\n } else {\n cands.forEach(function(candidate) {\n maybeAddCandidate(transceiver.iceTransport, candidate);\n });\n }\n }\n\n localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);\n\n // filter RTX until additional stuff needed for RTX is implemented\n // in adapter.js\n if (edgeVersion < 15019) {\n localCapabilities.codecs = localCapabilities.codecs.filter(\n function(codec) {\n return codec.name !== 'rtx';\n });\n }\n\n sendEncodingParameters = transceiver.sendEncodingParameters || [{\n ssrc: (2 * sdpMLineIndex + 2) * 1001\n }];\n\n // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams\n var isNewTrack = false;\n if (direction === 'sendrecv' || direction === 'sendonly') {\n isNewTrack = !transceiver.rtpReceiver;\n rtpReceiver = transceiver.rtpReceiver ||\n new window.RTCRtpReceiver(transceiver.dtlsTransport, kind);\n\n if (isNewTrack) {\n var stream;\n track = rtpReceiver.track;\n // FIXME: does not work with Plan B.\n if (remoteMsid && remoteMsid.stream === '-') {\n // no-op. a stream id of '-' means: no associated stream.\n } else if (remoteMsid) {\n if (!streams[remoteMsid.stream]) {\n streams[remoteMsid.stream] = new window.MediaStream();\n Object.defineProperty(streams[remoteMsid.stream], 'id', {\n get: function() {\n return remoteMsid.stream;\n }\n });\n }\n Object.defineProperty(track, 'id', {\n get: function() {\n return remoteMsid.track;\n }\n });\n stream = streams[remoteMsid.stream];\n } else {\n if (!streams.default) {\n streams.default = new window.MediaStream();\n }\n stream = streams.default;\n }\n if (stream) {\n addTrackToStreamAndFireEvent(track, stream);\n transceiver.associatedRemoteMediaStreams.push(stream);\n }\n receiverList.push([track, rtpReceiver, stream]);\n }\n } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) {\n transceiver.associatedRemoteMediaStreams.forEach(function(s) {\n var nativeTrack = s.getTracks().find(function(t) {\n return t.id === transceiver.rtpReceiver.track.id;\n });\n if (nativeTrack) {\n removeTrackFromStreamAndFireEvent(nativeTrack, s);\n }\n });\n transceiver.associatedRemoteMediaStreams = [];\n }\n\n transceiver.localCapabilities = localCapabilities;\n transceiver.remoteCapabilities = remoteCapabilities;\n transceiver.rtpReceiver = rtpReceiver;\n transceiver.rtcpParameters = rtcpParameters;\n transceiver.sendEncodingParameters = sendEncodingParameters;\n transceiver.recvEncodingParameters = recvEncodingParameters;\n\n // Start the RTCRtpReceiver now. The RTPSender is started in\n // setLocalDescription.\n pc._transceive(pc.transceivers[sdpMLineIndex],\n false,\n isNewTrack);\n } else if (description.type === 'answer' && !rejected) {\n transceiver = pc.transceivers[sdpMLineIndex];\n iceGatherer = transceiver.iceGatherer;\n iceTransport = transceiver.iceTransport;\n dtlsTransport = transceiver.dtlsTransport;\n rtpReceiver = transceiver.rtpReceiver;\n sendEncodingParameters = transceiver.sendEncodingParameters;\n localCapabilities = transceiver.localCapabilities;\n\n pc.transceivers[sdpMLineIndex].recvEncodingParameters =\n recvEncodingParameters;\n pc.transceivers[sdpMLineIndex].remoteCapabilities =\n remoteCapabilities;\n pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;\n\n if (cands.length && iceTransport.state === 'new') {\n if ((isIceLite || isComplete) &&\n (!usingBundle || sdpMLineIndex === 0)) {\n iceTransport.setRemoteCandidates(cands);\n } else {\n cands.forEach(function(candidate) {\n maybeAddCandidate(transceiver.iceTransport, candidate);\n });\n }\n }\n\n if (!usingBundle || sdpMLineIndex === 0) {\n if (iceTransport.state === 'new') {\n iceTransport.start(iceGatherer, remoteIceParameters,\n 'controlling');\n }\n if (dtlsTransport.state === 'new') {\n dtlsTransport.start(remoteDtlsParameters);\n }\n }\n\n // If the offer contained RTX but the answer did not,\n // remove RTX from sendEncodingParameters.\n var commonCapabilities = getCommonCapabilities(\n transceiver.localCapabilities,\n transceiver.remoteCapabilities);\n\n var hasRtx = commonCapabilities.codecs.filter(function(c) {\n return c.name.toLowerCase() === 'rtx';\n }).length;\n if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {\n delete transceiver.sendEncodingParameters[0].rtx;\n }\n\n pc._transceive(transceiver,\n direction === 'sendrecv' || direction === 'recvonly',\n direction === 'sendrecv' || direction === 'sendonly');\n\n // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams\n if (rtpReceiver &&\n (direction === 'sendrecv' || direction === 'sendonly')) {\n track = rtpReceiver.track;\n if (remoteMsid) {\n if (!streams[remoteMsid.stream]) {\n streams[remoteMsid.stream] = new window.MediaStream();\n }\n addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]);\n receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);\n } else {\n if (!streams.default) {\n streams.default = new window.MediaStream();\n }\n addTrackToStreamAndFireEvent(track, streams.default);\n receiverList.push([track, rtpReceiver, streams.default]);\n }\n } else {\n // FIXME: actually the receiver should be created later.\n delete transceiver.rtpReceiver;\n }\n }\n });\n\n if (pc._dtlsRole === undefined) {\n pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive';\n }\n\n pc._remoteDescription = {\n type: description.type,\n sdp: description.sdp\n };\n if (description.type === 'offer') {\n pc._updateSignalingState('have-remote-offer');\n } else {\n pc._updateSignalingState('stable');\n }\n Object.keys(streams).forEach(function(sid) {\n var stream = streams[sid];\n if (stream.getTracks().length) {\n if (pc.remoteStreams.indexOf(stream) === -1) {\n pc.remoteStreams.push(stream);\n var event = new Event('addstream');\n event.stream = stream;\n window.setTimeout(function() {\n pc._dispatchEvent('addstream', event);\n });\n }\n\n receiverList.forEach(function(item) {\n var track = item[0];\n var receiver = item[1];\n if (stream.id !== item[2].id) {\n return;\n }\n fireAddTrack(pc, track, receiver, [stream]);\n });\n }\n });\n receiverList.forEach(function(item) {\n if (item[2]) {\n return;\n }\n fireAddTrack(pc, item[0], item[1], []);\n });\n\n // check whether addIceCandidate({}) was called within four seconds after\n // setRemoteDescription.\n window.setTimeout(function() {\n if (!(pc && pc.transceivers)) {\n return;\n }\n pc.transceivers.forEach(function(transceiver) {\n if (transceiver.iceTransport &&\n transceiver.iceTransport.state === 'new' &&\n transceiver.iceTransport.getRemoteCandidates().length > 0) {\n console.warn('Timeout for addRemoteCandidate. Consider sending ' +\n 'an end-of-candidates notification');\n transceiver.iceTransport.addRemoteCandidate({});\n }\n });\n }, 4000);\n\n return Promise.resolve();\n };\n\n RTCPeerConnection.prototype.close = function() {\n this.transceivers.forEach(function(transceiver) {\n /* not yet\n if (transceiver.iceGatherer) {\n transceiver.iceGatherer.close();\n }\n */\n if (transceiver.iceTransport) {\n transceiver.iceTransport.stop();\n }\n if (transceiver.dtlsTransport) {\n transceiver.dtlsTransport.stop();\n }\n if (transceiver.rtpSender) {\n transceiver.rtpSender.stop();\n }\n if (transceiver.rtpReceiver) {\n transceiver.rtpReceiver.stop();\n }\n });\n // FIXME: clean up tracks, local streams, remote streams, etc\n this._isClosed = true;\n this._updateSignalingState('closed');\n };\n\n // Update the signaling state.\n RTCPeerConnection.prototype._updateSignalingState = function(newState) {\n this.signalingState = newState;\n var event = new Event('signalingstatechange');\n this._dispatchEvent('signalingstatechange', event);\n };\n\n // Determine whether to fire the negotiationneeded event.\n RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {\n var pc = this;\n if (this.signalingState !== 'stable' || this.needNegotiation === true) {\n return;\n }\n this.needNegotiation = true;\n window.setTimeout(function() {\n if (pc.needNegotiation) {\n pc.needNegotiation = false;\n var event = new Event('negotiationneeded');\n pc._dispatchEvent('negotiationneeded', event);\n }\n }, 0);\n };\n\n // Update the ice connection state.\n RTCPeerConnection.prototype._updateIceConnectionState = function() {\n var newState;\n var states = {\n 'new': 0,\n closed: 0,\n checking: 0,\n connected: 0,\n completed: 0,\n disconnected: 0,\n failed: 0\n };\n this.transceivers.forEach(function(transceiver) {\n if (transceiver.iceTransport && !transceiver.rejected) {\n states[transceiver.iceTransport.state]++;\n }\n });\n\n newState = 'new';\n if (states.failed > 0) {\n newState = 'failed';\n } else if (states.checking > 0) {\n newState = 'checking';\n } else if (states.disconnected > 0) {\n newState = 'disconnected';\n } else if (states.new > 0) {\n newState = 'new';\n } else if (states.connected > 0) {\n newState = 'connected';\n } else if (states.completed > 0) {\n newState = 'completed';\n }\n\n if (newState !== this.iceConnectionState) {\n this.iceConnectionState = newState;\n var event = new Event('iceconnectionstatechange');\n this._dispatchEvent('iceconnectionstatechange', event);\n }\n };\n\n // Update the connection state.\n RTCPeerConnection.prototype._updateConnectionState = function() {\n var newState;\n var states = {\n 'new': 0,\n closed: 0,\n connecting: 0,\n connected: 0,\n completed: 0,\n disconnected: 0,\n failed: 0\n };\n this.transceivers.forEach(function(transceiver) {\n if (transceiver.iceTransport && transceiver.dtlsTransport &&\n !transceiver.rejected) {\n states[transceiver.iceTransport.state]++;\n states[transceiver.dtlsTransport.state]++;\n }\n });\n // ICETransport.completed and connected are the same for this purpose.\n states.connected += states.completed;\n\n newState = 'new';\n if (states.failed > 0) {\n newState = 'failed';\n } else if (states.connecting > 0) {\n newState = 'connecting';\n } else if (states.disconnected > 0) {\n newState = 'disconnected';\n } else if (states.new > 0) {\n newState = 'new';\n } else if (states.connected > 0) {\n newState = 'connected';\n }\n\n if (newState !== this.connectionState) {\n this.connectionState = newState;\n var event = new Event('connectionstatechange');\n this._dispatchEvent('connectionstatechange', event);\n }\n };\n\n RTCPeerConnection.prototype.createOffer = function() {\n var pc = this;\n\n if (pc._isClosed) {\n return Promise.reject(makeError('InvalidStateError',\n 'Can not call createOffer after close'));\n }\n\n var numAudioTracks = pc.transceivers.filter(function(t) {\n return t.kind === 'audio';\n }).length;\n var numVideoTracks = pc.transceivers.filter(function(t) {\n return t.kind === 'video';\n }).length;\n\n // Determine number of audio and video tracks we need to send/recv.\n var offerOptions = arguments[0];\n if (offerOptions) {\n // Reject Chrome legacy constraints.\n if (offerOptions.mandatory || offerOptions.optional) {\n throw new TypeError(\n 'Legacy mandatory/optional constraints not supported.');\n }\n if (offerOptions.offerToReceiveAudio !== undefined) {\n if (offerOptions.offerToReceiveAudio === true) {\n numAudioTracks = 1;\n } else if (offerOptions.offerToReceiveAudio === false) {\n numAudioTracks = 0;\n } else {\n numAudioTracks = offerOptions.offerToReceiveAudio;\n }\n }\n if (offerOptions.offerToReceiveVideo !== undefined) {\n if (offerOptions.offerToReceiveVideo === true) {\n numVideoTracks = 1;\n } else if (offerOptions.offerToReceiveVideo === false) {\n numVideoTracks = 0;\n } else {\n numVideoTracks = offerOptions.offerToReceiveVideo;\n }\n }\n }\n\n pc.transceivers.forEach(function(transceiver) {\n if (transceiver.kind === 'audio') {\n numAudioTracks--;\n if (numAudioTracks < 0) {\n transceiver.wantReceive = false;\n }\n } else if (transceiver.kind === 'video') {\n numVideoTracks--;\n if (numVideoTracks < 0) {\n transceiver.wantReceive = false;\n }\n }\n });\n\n // Create M-lines for recvonly streams.\n while (numAudioTracks > 0 || numVideoTracks > 0) {\n if (numAudioTracks > 0) {\n pc._createTransceiver('audio');\n numAudioTracks--;\n }\n if (numVideoTracks > 0) {\n pc._createTransceiver('video');\n numVideoTracks--;\n }\n }\n\n var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,\n pc._sdpSessionVersion++);\n pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {\n // For each track, create an ice gatherer, ice transport,\n // dtls transport, potentially rtpsender and rtpreceiver.\n var track = transceiver.track;\n var kind = transceiver.kind;\n var mid = transceiver.mid || SDPUtils.generateIdentifier();\n transceiver.mid = mid;\n\n if (!transceiver.iceGatherer) {\n transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,\n pc.usingBundle);\n }\n\n var localCapabilities = window.RTCRtpSender.getCapabilities(kind);\n // filter RTX until additional stuff needed for RTX is implemented\n // in adapter.js\n if (edgeVersion < 15019) {\n localCapabilities.codecs = localCapabilities.codecs.filter(\n function(codec) {\n return codec.name !== 'rtx';\n });\n }\n localCapabilities.codecs.forEach(function(codec) {\n // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552\n // by adding level-asymmetry-allowed=1\n if (codec.name === 'H264' &&\n codec.parameters['level-asymmetry-allowed'] === undefined) {\n codec.parameters['level-asymmetry-allowed'] = '1';\n }\n\n // for subsequent offers, we might have to re-use the payload\n // type of the last offer.\n if (transceiver.remoteCapabilities &&\n transceiver.remoteCapabilities.codecs) {\n transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec) {\n if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() &&\n codec.clockRate === remoteCodec.clockRate) {\n codec.preferredPayloadType = remoteCodec.payloadType;\n }\n });\n }\n });\n localCapabilities.headerExtensions.forEach(function(hdrExt) {\n var remoteExtensions = transceiver.remoteCapabilities &&\n transceiver.remoteCapabilities.headerExtensions || [];\n remoteExtensions.forEach(function(rHdrExt) {\n if (hdrExt.uri === rHdrExt.uri) {\n hdrExt.id = rHdrExt.id;\n }\n });\n });\n\n // generate an ssrc now, to be used later in rtpSender.send\n var sendEncodingParameters = transceiver.sendEncodingParameters || [{\n ssrc: (2 * sdpMLineIndex + 1) * 1001\n }];\n if (track) {\n // add RTX\n if (edgeVersion >= 15019 && kind === 'video' &&\n !sendEncodingParameters[0].rtx) {\n sendEncodingParameters[0].rtx = {\n ssrc: sendEncodingParameters[0].ssrc + 1\n };\n }\n }\n\n if (transceiver.wantReceive) {\n transceiver.rtpReceiver = new window.RTCRtpReceiver(\n transceiver.dtlsTransport, kind);\n }\n\n transceiver.localCapabilities = localCapabilities;\n transceiver.sendEncodingParameters = sendEncodingParameters;\n });\n\n // always offer BUNDLE and dispose on return if not supported.\n if (pc._config.bundlePolicy !== 'max-compat') {\n sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {\n return t.mid;\n }).join(' ') + '\\r\\n';\n }\n sdp += 'a=ice-options:trickle\\r\\n';\n\n pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {\n sdp += writeMediaSection(transceiver, transceiver.localCapabilities,\n 'offer', transceiver.stream, pc._dtlsRole);\n sdp += 'a=rtcp-rsize\\r\\n';\n\n if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' &&\n (sdpMLineIndex === 0 || !pc.usingBundle)) {\n transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) {\n cand.component = 1;\n sdp += 'a=' + SDPUtils.writeCandidate(cand) + '\\r\\n';\n });\n\n if (transceiver.iceGatherer.state === 'completed') {\n sdp += 'a=end-of-candidates\\r\\n';\n }\n }\n });\n\n var desc = new window.RTCSessionDescription({\n type: 'offer',\n sdp: sdp\n });\n return Promise.resolve(desc);\n };\n\n RTCPeerConnection.prototype.createAnswer = function() {\n var pc = this;\n\n if (pc._isClosed) {\n return Promise.reject(makeError('InvalidStateError',\n 'Can not call createAnswer after close'));\n }\n\n if (!(pc.signalingState === 'have-remote-offer' ||\n pc.signalingState === 'have-local-pranswer')) {\n return Promise.reject(makeError('InvalidStateError',\n 'Can not call createAnswer in signalingState ' + pc.signalingState));\n }\n\n var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,\n pc._sdpSessionVersion++);\n if (pc.usingBundle) {\n sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {\n return t.mid;\n }).join(' ') + '\\r\\n';\n }\n sdp += 'a=ice-options:trickle\\r\\n';\n\n var mediaSectionsInOffer = SDPUtils.getMediaSections(\n pc._remoteDescription.sdp).length;\n pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {\n if (sdpMLineIndex + 1 > mediaSectionsInOffer) {\n return;\n }\n if (transceiver.rejected) {\n if (transceiver.kind === 'application') {\n if (transceiver.protocol === 'DTLS/SCTP') { // legacy fmt\n sdp += 'm=application 0 DTLS/SCTP 5000\\r\\n';\n } else {\n sdp += 'm=application 0 ' + transceiver.protocol +\n ' webrtc-datachannel\\r\\n';\n }\n } else if (transceiver.kind === 'audio') {\n sdp += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\\r\\n' +\n 'a=rtpmap:0 PCMU/8000\\r\\n';\n } else if (transceiver.kind === 'video') {\n sdp += 'm=video 0 UDP/TLS/RTP/SAVPF 120\\r\\n' +\n 'a=rtpmap:120 VP8/90000\\r\\n';\n }\n sdp += 'c=IN IP4 0.0.0.0\\r\\n' +\n 'a=inactive\\r\\n' +\n 'a=mid:' + transceiver.mid + '\\r\\n';\n return;\n }\n\n // FIXME: look at direction.\n if (transceiver.stream) {\n var localTrack;\n if (transceiver.kind === 'audio') {\n localTrack = transceiver.stream.getAudioTracks()[0];\n } else if (transceiver.kind === 'video') {\n localTrack = transceiver.stream.getVideoTracks()[0];\n }\n if (localTrack) {\n // add RTX\n if (edgeVersion >= 15019 && transceiver.kind === 'video' &&\n !transceiver.sendEncodingParameters[0].rtx) {\n transceiver.sendEncodingParameters[0].rtx = {\n ssrc: transceiver.sendEncodingParameters[0].ssrc + 1\n };\n }\n }\n }\n\n // Calculate intersection of capabilities.\n var commonCapabilities = getCommonCapabilities(\n transceiver.localCapabilities,\n transceiver.remoteCapabilities);\n\n var hasRtx = commonCapabilities.codecs.filter(function(c) {\n return c.name.toLowerCase() === 'rtx';\n }).length;\n if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {\n delete transceiver.sendEncodingParameters[0].rtx;\n }\n\n sdp += writeMediaSection(transceiver, commonCapabilities,\n 'answer', transceiver.stream, pc._dtlsRole);\n if (transceiver.rtcpParameters &&\n transceiver.rtcpParameters.reducedSize) {\n sdp += 'a=rtcp-rsize\\r\\n';\n }\n });\n\n var desc = new window.RTCSessionDescription({\n type: 'answer',\n sdp: sdp\n });\n return Promise.resolve(desc);\n };\n\n RTCPeerConnection.prototype.addIceCandidate = function(candidate) {\n var pc = this;\n var sections;\n if (candidate && !(candidate.sdpMLineIndex !== undefined ||\n candidate.sdpMid)) {\n return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required'));\n }\n\n // TODO: needs to go into ops queue.\n return new Promise(function(resolve, reject) {\n if (!pc._remoteDescription) {\n return reject(makeError('InvalidStateError',\n 'Can not add ICE candidate without a remote description'));\n } else if (!candidate || candidate.candidate === '') {\n for (var j = 0; j < pc.transceivers.length; j++) {\n if (pc.transceivers[j].rejected) {\n continue;\n }\n pc.transceivers[j].iceTransport.addRemoteCandidate({});\n sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);\n sections[j] += 'a=end-of-candidates\\r\\n';\n pc._remoteDescription.sdp =\n SDPUtils.getDescription(pc._remoteDescription.sdp) +\n sections.join('');\n if (pc.usingBundle) {\n break;\n }\n }\n } else {\n var sdpMLineIndex = candidate.sdpMLineIndex;\n if (candidate.sdpMid) {\n for (var i = 0; i < pc.transceivers.length; i++) {\n if (pc.transceivers[i].mid === candidate.sdpMid) {\n sdpMLineIndex = i;\n break;\n }\n }\n }\n var transceiver = pc.transceivers[sdpMLineIndex];\n if (transceiver) {\n if (transceiver.rejected) {\n return resolve();\n }\n var cand = Object.keys(candidate.candidate).length > 0 ?\n SDPUtils.parseCandidate(candidate.candidate) : {};\n // Ignore Chrome's invalid candidates since Edge does not like them.\n if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {\n return resolve();\n }\n // Ignore RTCP candidates, we assume RTCP-MUX.\n if (cand.component && cand.component !== 1) {\n return resolve();\n }\n // when using bundle, avoid adding candidates to the wrong\n // ice transport. And avoid adding candidates added in the SDP.\n if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 &&\n transceiver.iceTransport !== pc.transceivers[0].iceTransport)) {\n if (!maybeAddCandidate(transceiver.iceTransport, cand)) {\n return reject(makeError('OperationError',\n 'Can not add ICE candidate'));\n }\n }\n\n // update the remoteDescription.\n var candidateString = candidate.candidate.trim();\n if (candidateString.indexOf('a=') === 0) {\n candidateString = candidateString.substr(2);\n }\n sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);\n sections[sdpMLineIndex] += 'a=' +\n (cand.type ? candidateString : 'end-of-candidates')\n + '\\r\\n';\n pc._remoteDescription.sdp =\n SDPUtils.getDescription(pc._remoteDescription.sdp) +\n sections.join('');\n } else {\n return reject(makeError('OperationError',\n 'Can not add ICE candidate'));\n }\n }\n resolve();\n });\n };\n\n RTCPeerConnection.prototype.getStats = function(selector) {\n if (selector && selector instanceof window.MediaStreamTrack) {\n var senderOrReceiver = null;\n this.transceivers.forEach(function(transceiver) {\n if (transceiver.rtpSender &&\n transceiver.rtpSender.track === selector) {\n senderOrReceiver = transceiver.rtpSender;\n } else if (transceiver.rtpReceiver &&\n transceiver.rtpReceiver.track === selector) {\n senderOrReceiver = transceiver.rtpReceiver;\n }\n });\n if (!senderOrReceiver) {\n throw makeError('InvalidAccessError', 'Invalid selector.');\n }\n return senderOrReceiver.getStats();\n }\n\n var promises = [];\n this.transceivers.forEach(function(transceiver) {\n ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',\n 'dtlsTransport'].forEach(function(method) {\n if (transceiver[method]) {\n promises.push(transceiver[method].getStats());\n }\n });\n });\n return Promise.all(promises).then(function(allStats) {\n var results = new Map();\n allStats.forEach(function(stats) {\n stats.forEach(function(stat) {\n results.set(stat.id, stat);\n });\n });\n return results;\n });\n };\n\n // fix low-level stat names and return Map instead of object.\n var ortcObjects = ['RTCRtpSender', 'RTCRtpReceiver', 'RTCIceGatherer',\n 'RTCIceTransport', 'RTCDtlsTransport'];\n ortcObjects.forEach(function(ortcObjectName) {\n var obj = window[ortcObjectName];\n if (obj && obj.prototype && obj.prototype.getStats) {\n var nativeGetstats = obj.prototype.getStats;\n obj.prototype.getStats = function() {\n return nativeGetstats.apply(this)\n .then(function(nativeStats) {\n var mapStats = new Map();\n Object.keys(nativeStats).forEach(function(id) {\n nativeStats[id].type = fixStatsType(nativeStats[id]);\n mapStats.set(id, nativeStats[id]);\n });\n return mapStats;\n });\n };\n }\n });\n\n // legacy callback shims. Should be moved to adapter.js some days.\n var methods = ['createOffer', 'createAnswer'];\n methods.forEach(function(method) {\n var nativeMethod = RTCPeerConnection.prototype[method];\n RTCPeerConnection.prototype[method] = function() {\n var args = arguments;\n if (typeof args[0] === 'function' ||\n typeof args[1] === 'function') { // legacy\n return nativeMethod.apply(this, [arguments[2]])\n .then(function(description) {\n if (typeof args[0] === 'function') {\n args[0].apply(null, [description]);\n }\n }, function(error) {\n if (typeof args[1] === 'function') {\n args[1].apply(null, [error]);\n }\n });\n }\n return nativeMethod.apply(this, arguments);\n };\n });\n\n methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'];\n methods.forEach(function(method) {\n var nativeMethod = RTCPeerConnection.prototype[method];\n RTCPeerConnection.prototype[method] = function() {\n var args = arguments;\n if (typeof args[1] === 'function' ||\n typeof args[2] === 'function') { // legacy\n return nativeMethod.apply(this, arguments)\n .then(function() {\n if (typeof args[1] === 'function') {\n args[1].apply(null);\n }\n }, function(error) {\n if (typeof args[2] === 'function') {\n args[2].apply(null, [error]);\n }\n });\n }\n return nativeMethod.apply(this, arguments);\n };\n });\n\n // getStats is special. It doesn't have a spec legacy method yet we support\n // getStats(something, cb) without error callbacks.\n ['getStats'].forEach(function(method) {\n var nativeMethod = RTCPeerConnection.prototype[method];\n RTCPeerConnection.prototype[method] = function() {\n var args = arguments;\n if (typeof args[1] === 'function') {\n return nativeMethod.apply(this, arguments)\n .then(function() {\n if (typeof args[1] === 'function') {\n args[1].apply(null);\n }\n });\n }\n return nativeMethod.apply(this, arguments);\n };\n });\n\n return RTCPeerConnection;\n};\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n'use strict';\n\nexport function shimGetUserMedia(window) {\n const navigator = window && window.navigator;\n\n const shimError_ = function(e) {\n return {\n name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,\n message: e.message,\n constraint: e.constraint,\n toString() {\n return this.name;\n }\n };\n };\n\n // getUserMedia error shim.\n const origGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(c) {\n return origGetUserMedia(c).catch(e => Promise.reject(shimError_(e)));\n };\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n /* eslint-env node */\n'use strict';\n\nexport function shimGetDisplayMedia(window) {\n if (!('getDisplayMedia' in window.navigator)) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n window.navigator.getDisplayMedia.bind(window.navigator);\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\nimport {filterIceServers} from './filtericeservers';\nimport shimRTCPeerConnection from 'rtcpeerconnection-shim';\n\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimPeerConnection(window, browserDetails) {\n if (window.RTCIceGatherer) {\n if (!window.RTCIceCandidate) {\n window.RTCIceCandidate = function RTCIceCandidate(args) {\n return args;\n };\n }\n if (!window.RTCSessionDescription) {\n window.RTCSessionDescription = function RTCSessionDescription(args) {\n return args;\n };\n }\n // this adds an additional event listener to MediaStrackTrack that signals\n // when a tracks enabled property was changed. Workaround for a bug in\n // addStream, see below. No longer required in 15025+\n if (browserDetails.version < 15025) {\n const origMSTEnabled = Object.getOwnPropertyDescriptor(\n window.MediaStreamTrack.prototype, 'enabled');\n Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {\n set(value) {\n origMSTEnabled.set.call(this, value);\n const ev = new Event('enabled');\n ev.enabled = value;\n this.dispatchEvent(ev);\n }\n });\n }\n }\n\n // ORTC defines the DTMF sender a bit different.\n // https://github.com/w3c/ortc/issues/714\n if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {\n Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {\n get() {\n if (this._dtmf === undefined) {\n if (this.track.kind === 'audio') {\n this._dtmf = new window.RTCDtmfSender(this);\n } else if (this.track.kind === 'video') {\n this._dtmf = null;\n }\n }\n return this._dtmf;\n }\n });\n }\n // Edge currently only implements the RTCDtmfSender, not the\n // RTCDTMFSender alias. See http://draft.ortc.org/#rtcdtmfsender2*\n if (window.RTCDtmfSender && !window.RTCDTMFSender) {\n window.RTCDTMFSender = window.RTCDtmfSender;\n }\n\n const RTCPeerConnectionShim = shimRTCPeerConnection(window,\n browserDetails.version);\n window.RTCPeerConnection = function RTCPeerConnection(config) {\n if (config && config.iceServers) {\n config.iceServers = filterIceServers(config.iceServers,\n browserDetails.version);\n utils.log('ICE servers after filtering:', config.iceServers);\n }\n return new RTCPeerConnectionShim(config);\n };\n window.RTCPeerConnection.prototype = RTCPeerConnectionShim.prototype;\n}\n\nexport function shimReplaceTrack(window) {\n // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614\n if (window.RTCRtpSender &&\n !('replaceTrack' in window.RTCRtpSender.prototype)) {\n window.RTCRtpSender.prototype.replaceTrack =\n window.RTCRtpSender.prototype.setTrack;\n }\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\n\nexport function shimGetUserMedia(window, browserDetails) {\n const navigator = window && window.navigator;\n const MediaStreamTrack = window && window.MediaStreamTrack;\n\n navigator.getUserMedia = function(constraints, onSuccess, onError) {\n // Replace Firefox 44+'s deprecation warning with unprefixed version.\n utils.deprecated('navigator.getUserMedia',\n 'navigator.mediaDevices.getUserMedia');\n navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);\n };\n\n if (!(browserDetails.version > 55 &&\n 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {\n const remap = function(obj, a, b) {\n if (a in obj && !(b in obj)) {\n obj[b] = obj[a];\n delete obj[a];\n }\n };\n\n const nativeGetUserMedia = navigator.mediaDevices.getUserMedia.\n bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = function(c) {\n if (typeof c === 'object' && typeof c.audio === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c.audio, 'autoGainControl', 'mozAutoGainControl');\n remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeGetUserMedia(c);\n };\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {\n const nativeGetSettings = MediaStreamTrack.prototype.getSettings;\n MediaStreamTrack.prototype.getSettings = function() {\n const obj = nativeGetSettings.apply(this, arguments);\n remap(obj, 'mozAutoGainControl', 'autoGainControl');\n remap(obj, 'mozNoiseSuppression', 'noiseSuppression');\n return obj;\n };\n }\n\n if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {\n const nativeApplyConstraints =\n MediaStreamTrack.prototype.applyConstraints;\n MediaStreamTrack.prototype.applyConstraints = function(c) {\n if (this.kind === 'audio' && typeof c === 'object') {\n c = JSON.parse(JSON.stringify(c));\n remap(c, 'autoGainControl', 'mozAutoGainControl');\n remap(c, 'noiseSuppression', 'mozNoiseSuppression');\n }\n return nativeApplyConstraints.apply(this, [c]);\n };\n }\n }\n}\n","/*\n * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nexport function shimGetDisplayMedia(window, preferredMediaSource) {\n if (window.navigator.mediaDevices &&\n 'getDisplayMedia' in window.navigator.mediaDevices) {\n return;\n }\n if (!(window.navigator.mediaDevices)) {\n return;\n }\n window.navigator.mediaDevices.getDisplayMedia =\n function getDisplayMedia(constraints) {\n if (!(constraints && constraints.video)) {\n const err = new DOMException('getDisplayMedia without video ' +\n 'constraints is undefined');\n err.name = 'NotFoundError';\n // from https://heycam.github.io/webidl/#idl-DOMException-error-names\n err.code = 8;\n return Promise.reject(err);\n }\n if (constraints.video === true) {\n constraints.video = {mediaSource: preferredMediaSource};\n } else {\n constraints.video.mediaSource = preferredMediaSource;\n }\n return window.navigator.mediaDevices.getUserMedia(constraints);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimOnTrack(window) {\n if (typeof window === 'object' && window.RTCTrackEvent &&\n ('receiver' in window.RTCTrackEvent.prototype) &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n if (typeof window !== 'object' ||\n !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {\n return; // probably media.peerconnection.enabled=false in about:config\n }\n if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {\n // very basic support for old versions.\n window.RTCPeerConnection = window.mozRTCPeerConnection;\n }\n\n if (browserDetails.version < 53) {\n // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.\n ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n .forEach(function(method) {\n const nativeMethod = window.RTCPeerConnection.prototype[method];\n const methodObj = {[method]() {\n arguments[0] = new ((method === 'addIceCandidate') ?\n window.RTCIceCandidate :\n window.RTCSessionDescription)(arguments[0]);\n return nativeMethod.apply(this, arguments);\n }};\n window.RTCPeerConnection.prototype[method] = methodObj[method];\n });\n }\n\n const modernStatsTypes = {\n inboundrtp: 'inbound-rtp',\n outboundrtp: 'outbound-rtp',\n candidatepair: 'candidate-pair',\n localcandidate: 'local-candidate',\n remotecandidate: 'remote-candidate'\n };\n\n const nativeGetStats = window.RTCPeerConnection.prototype.getStats;\n window.RTCPeerConnection.prototype.getStats = function getStats() {\n const [selector, onSucc, onErr] = arguments;\n return nativeGetStats.apply(this, [selector || null])\n .then(stats => {\n if (browserDetails.version < 53 && !onSucc) {\n // Shim only promise getStats with spec-hyphens in type names\n // Leave callback version alone; misc old uses of forEach before Map\n try {\n stats.forEach(stat => {\n stat.type = modernStatsTypes[stat.type] || stat.type;\n });\n } catch (e) {\n if (e.name !== 'TypeError') {\n throw e;\n }\n // Avoid TypeError: \"type\" is read-only, in old versions. 34-43ish\n stats.forEach((stat, i) => {\n stats.set(i, Object.assign({}, stat, {\n type: modernStatsTypes[stat.type] || stat.type\n }));\n });\n }\n }\n return stats;\n })\n .then(onSucc, onErr);\n };\n}\n\nexport function shimSenderGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {\n return;\n }\n const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n if (origGetSenders) {\n window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n const senders = origGetSenders.apply(this, []);\n senders.forEach(sender => sender._pc = this);\n return senders;\n };\n }\n\n const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n if (origAddTrack) {\n window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n const sender = origAddTrack.apply(this, arguments);\n sender._pc = this;\n return sender;\n };\n }\n window.RTCRtpSender.prototype.getStats = function getStats() {\n return this.track ? this._pc.getStats(this.track) :\n Promise.resolve(new Map());\n };\n}\n\nexport function shimReceiverGetStats(window) {\n if (!(typeof window === 'object' && window.RTCPeerConnection &&\n window.RTCRtpSender)) {\n return;\n }\n if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {\n return;\n }\n const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n if (origGetReceivers) {\n window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {\n const receivers = origGetReceivers.apply(this, []);\n receivers.forEach(receiver => receiver._pc = this);\n return receivers;\n };\n }\n utils.wrapPeerConnectionEvent(window, 'track', e => {\n e.receiver._pc = e.srcElement;\n return e;\n });\n window.RTCRtpReceiver.prototype.getStats = function getStats() {\n return this._pc.getStats(this.track);\n };\n}\n\nexport function shimRemoveStream(window) {\n if (!window.RTCPeerConnection ||\n 'removeStream' in window.RTCPeerConnection.prototype) {\n return;\n }\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n utils.deprecated('removeStream', 'removeTrack');\n this.getSenders().forEach(sender => {\n if (sender.track && stream.getTracks().includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n}\n\nexport function shimRTCDataChannel(window) {\n // rename DataChannel to RTCDataChannel (native fix in FF60):\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851\n if (window.DataChannel && !window.RTCDataChannel) {\n window.RTCDataChannel = window.DataChannel;\n }\n}\n\nexport function shimAddTransceiver(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;\n if (origAddTransceiver) {\n window.RTCPeerConnection.prototype.addTransceiver =\n function addTransceiver() {\n this.setParametersPromises = [];\n const initParameters = arguments[1];\n const shouldPerformCheck = initParameters &&\n 'sendEncodings' in initParameters;\n if (shouldPerformCheck) {\n // If sendEncodings params are provided, validate grammar\n initParameters.sendEncodings.forEach((encodingParam) => {\n if ('rid' in encodingParam) {\n const ridRegex = /^[a-z0-9]{0,16}$/i;\n if (!ridRegex.test(encodingParam.rid)) {\n throw new TypeError('Invalid RID value provided.');\n }\n }\n if ('scaleResolutionDownBy' in encodingParam) {\n if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {\n throw new RangeError('scale_resolution_down_by must be >= 1.0');\n }\n }\n if ('maxFramerate' in encodingParam) {\n if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {\n throw new RangeError('max_framerate must be >= 0.0');\n }\n }\n });\n }\n const transceiver = origAddTransceiver.apply(this, arguments);\n if (shouldPerformCheck) {\n // Check if the init options were applied. If not we do this in an\n // asynchronous way and save the promise reference in a global object.\n // This is an ugly hack, but at the same time is way more robust than\n // checking the sender parameters before and after the createOffer\n // Also note that after the createoffer we are not 100% sure that\n // the params were asynchronously applied so we might miss the\n // opportunity to recreate offer.\n const {sender} = transceiver;\n const params = sender.getParameters();\n if (!('encodings' in params) ||\n // Avoid being fooled by patched getParameters() below.\n (params.encodings.length === 1 &&\n Object.keys(params.encodings[0]).length === 0)) {\n params.encodings = initParameters.sendEncodings;\n sender.sendEncodings = initParameters.sendEncodings;\n this.setParametersPromises.push(sender.setParameters(params)\n .then(() => {\n delete sender.sendEncodings;\n }).catch(() => {\n delete sender.sendEncodings;\n })\n );\n }\n }\n return transceiver;\n };\n }\n}\n\nexport function shimGetParameters(window) {\n if (!(typeof window === 'object' && window.RTCRtpSender)) {\n return;\n }\n const origGetParameters = window.RTCRtpSender.prototype.getParameters;\n if (origGetParameters) {\n window.RTCRtpSender.prototype.getParameters =\n function getParameters() {\n const params = origGetParameters.apply(this, arguments);\n if (!('encodings' in params)) {\n params.encodings = [].concat(this.sendEncodings || [{}]);\n }\n return params;\n };\n }\n}\n\nexport function shimCreateOffer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer = function createOffer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateOffer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimCreateAnswer(window) {\n // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n // Firefox ignores the init sendEncodings options passed to addTransceiver\n // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n return;\n }\n const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;\n window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {\n if (this.setParametersPromises && this.setParametersPromises.length) {\n return Promise.all(this.setParametersPromises)\n .then(() => {\n return origCreateAnswer.apply(this, arguments);\n })\n .finally(() => {\n this.setParametersPromises = [];\n });\n }\n return origCreateAnswer.apply(this, arguments);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n'use strict';\nimport * as utils from '../utils';\n\nexport function shimLocalStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getLocalStreams =\n function getLocalStreams() {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n return this._localStreams;\n };\n }\n if (!('addStream' in window.RTCPeerConnection.prototype)) {\n const _addTrack = window.RTCPeerConnection.prototype.addTrack;\n window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n // Try to emulate Chrome's behaviour of adding in audio-video order.\n // Safari orders by track id.\n stream.getAudioTracks().forEach(track => _addTrack.call(this, track,\n stream));\n stream.getVideoTracks().forEach(track => _addTrack.call(this, track,\n stream));\n };\n\n window.RTCPeerConnection.prototype.addTrack =\n function addTrack(track, ...streams) {\n if (streams) {\n streams.forEach((stream) => {\n if (!this._localStreams) {\n this._localStreams = [stream];\n } else if (!this._localStreams.includes(stream)) {\n this._localStreams.push(stream);\n }\n });\n }\n return _addTrack.apply(this, arguments);\n };\n }\n if (!('removeStream' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.removeStream =\n function removeStream(stream) {\n if (!this._localStreams) {\n this._localStreams = [];\n }\n const index = this._localStreams.indexOf(stream);\n if (index === -1) {\n return;\n }\n this._localStreams.splice(index, 1);\n const tracks = stream.getTracks();\n this.getSenders().forEach(sender => {\n if (tracks.includes(sender.track)) {\n this.removeTrack(sender);\n }\n });\n };\n }\n}\n\nexport function shimRemoteStreamsAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {\n window.RTCPeerConnection.prototype.getRemoteStreams =\n function getRemoteStreams() {\n return this._remoteStreams ? this._remoteStreams : [];\n };\n }\n if (!('onaddstream' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {\n get() {\n return this._onaddstream;\n },\n set(f) {\n if (this._onaddstream) {\n this.removeEventListener('addstream', this._onaddstream);\n this.removeEventListener('track', this._onaddstreampoly);\n }\n this.addEventListener('addstream', this._onaddstream = f);\n this.addEventListener('track', this._onaddstreampoly = (e) => {\n e.streams.forEach(stream => {\n if (!this._remoteStreams) {\n this._remoteStreams = [];\n }\n if (this._remoteStreams.includes(stream)) {\n return;\n }\n this._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n this.dispatchEvent(event);\n });\n });\n }\n });\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n const pc = this;\n if (!this._onaddstreampoly) {\n this.addEventListener('track', this._onaddstreampoly = function(e) {\n e.streams.forEach(stream => {\n if (!pc._remoteStreams) {\n pc._remoteStreams = [];\n }\n if (pc._remoteStreams.indexOf(stream) >= 0) {\n return;\n }\n pc._remoteStreams.push(stream);\n const event = new Event('addstream');\n event.stream = stream;\n pc.dispatchEvent(event);\n });\n });\n }\n return origSetRemoteDescription.apply(pc, arguments);\n };\n }\n}\n\nexport function shimCallbacksAPI(window) {\n if (typeof window !== 'object' || !window.RTCPeerConnection) {\n return;\n }\n const prototype = window.RTCPeerConnection.prototype;\n const origCreateOffer = prototype.createOffer;\n const origCreateAnswer = prototype.createAnswer;\n const setLocalDescription = prototype.setLocalDescription;\n const setRemoteDescription = prototype.setRemoteDescription;\n const addIceCandidate = prototype.addIceCandidate;\n\n prototype.createOffer =\n function createOffer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateOffer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n prototype.createAnswer =\n function createAnswer(successCallback, failureCallback) {\n const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n const promise = origCreateAnswer.apply(this, [options]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n\n let withCallback = function(description, successCallback, failureCallback) {\n const promise = setLocalDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setLocalDescription = withCallback;\n\n withCallback = function(description, successCallback, failureCallback) {\n const promise = setRemoteDescription.apply(this, [description]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.setRemoteDescription = withCallback;\n\n withCallback = function(candidate, successCallback, failureCallback) {\n const promise = addIceCandidate.apply(this, [candidate]);\n if (!failureCallback) {\n return promise;\n }\n promise.then(successCallback, failureCallback);\n return Promise.resolve();\n };\n prototype.addIceCandidate = withCallback;\n}\n\nexport function shimGetUserMedia(window) {\n const navigator = window && window.navigator;\n\n if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n // shim not needed in Safari 12.1\n const mediaDevices = navigator.mediaDevices;\n const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);\n navigator.mediaDevices.getUserMedia = (constraints) => {\n return _getUserMedia(shimConstraints(constraints));\n };\n }\n\n if (!navigator.getUserMedia && navigator.mediaDevices &&\n navigator.mediaDevices.getUserMedia) {\n navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {\n navigator.mediaDevices.getUserMedia(constraints)\n .then(cb, errcb);\n }.bind(navigator);\n }\n}\n\nexport function shimConstraints(constraints) {\n if (constraints && constraints.video !== undefined) {\n return Object.assign({},\n constraints,\n {video: utils.compactObject(constraints.video)}\n );\n }\n\n return constraints;\n}\n\nexport function shimRTCIceServerUrls(window) {\n if (!window.RTCPeerConnection) {\n return;\n }\n // migrate from non-spec RTCIceServer.url to RTCIceServer.urls\n const OrigPeerConnection = window.RTCPeerConnection;\n window.RTCPeerConnection =\n function RTCPeerConnection(pcConfig, pcConstraints) {\n if (pcConfig && pcConfig.iceServers) {\n const newIceServers = [];\n for (let i = 0; i < pcConfig.iceServers.length; i++) {\n let server = pcConfig.iceServers[i];\n if (!server.hasOwnProperty('urls') &&\n server.hasOwnProperty('url')) {\n utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');\n server = JSON.parse(JSON.stringify(server));\n server.urls = server.url;\n delete server.url;\n newIceServers.push(server);\n } else {\n newIceServers.push(pcConfig.iceServers[i]);\n }\n }\n pcConfig.iceServers = newIceServers;\n }\n return new OrigPeerConnection(pcConfig, pcConstraints);\n };\n window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;\n // wrap static methods. Currently just generateCertificate.\n if ('generateCertificate' in OrigPeerConnection) {\n Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {\n get() {\n return OrigPeerConnection.generateCertificate;\n }\n });\n }\n}\n\nexport function shimTrackEventTransceiver(window) {\n // Add event.transceiver member over deprecated event.receiver\n if (typeof window === 'object' && window.RTCTrackEvent &&\n 'receiver' in window.RTCTrackEvent.prototype &&\n !('transceiver' in window.RTCTrackEvent.prototype)) {\n Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n get() {\n return {receiver: this.receiver};\n }\n });\n }\n}\n\nexport function shimCreateOfferLegacy(window) {\n const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n window.RTCPeerConnection.prototype.createOffer =\n function createOffer(offerOptions) {\n if (offerOptions) {\n if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveAudio =\n !!offerOptions.offerToReceiveAudio;\n }\n const audioTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'audio');\n if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {\n if (audioTransceiver.direction === 'sendrecv') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('sendonly');\n } else {\n audioTransceiver.direction = 'sendonly';\n }\n } else if (audioTransceiver.direction === 'recvonly') {\n if (audioTransceiver.setDirection) {\n audioTransceiver.setDirection('inactive');\n } else {\n audioTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveAudio === true &&\n !audioTransceiver) {\n this.addTransceiver('audio');\n }\n\n if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {\n // support bit values\n offerOptions.offerToReceiveVideo =\n !!offerOptions.offerToReceiveVideo;\n }\n const videoTransceiver = this.getTransceivers().find(transceiver =>\n transceiver.receiver.track.kind === 'video');\n if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {\n if (videoTransceiver.direction === 'sendrecv') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('sendonly');\n } else {\n videoTransceiver.direction = 'sendonly';\n }\n } else if (videoTransceiver.direction === 'recvonly') {\n if (videoTransceiver.setDirection) {\n videoTransceiver.setDirection('inactive');\n } else {\n videoTransceiver.direction = 'inactive';\n }\n }\n } else if (offerOptions.offerToReceiveVideo === true &&\n !videoTransceiver) {\n this.addTransceiver('video');\n }\n }\n return origCreateOffer.apply(this, arguments);\n };\n}\n\nexport function shimAudioContext(window) {\n if (typeof window !== 'object' || window.AudioContext) {\n return;\n }\n window.AudioContext = window.webkitAudioContext;\n}\n","/*\n * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport SDPUtils from 'sdp';\nimport * as utils from './utils';\n\nexport function shimRTCIceCandidate(window) {\n // foundation is arbitrarily chosen as an indicator for full support for\n // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface\n if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in\n window.RTCIceCandidate.prototype)) {\n return;\n }\n\n const NativeRTCIceCandidate = window.RTCIceCandidate;\n window.RTCIceCandidate = function RTCIceCandidate(args) {\n // Remove the a= which shouldn't be part of the candidate string.\n if (typeof args === 'object' && args.candidate &&\n args.candidate.indexOf('a=') === 0) {\n args = JSON.parse(JSON.stringify(args));\n args.candidate = args.candidate.substr(2);\n }\n\n if (args.candidate && args.candidate.length) {\n // Augment the native candidate with the parsed fields.\n const nativeCandidate = new NativeRTCIceCandidate(args);\n const parsedCandidate = SDPUtils.parseCandidate(args.candidate);\n const augmentedCandidate = Object.assign(nativeCandidate,\n parsedCandidate);\n\n // Add a serializer that does not serialize the extra attributes.\n augmentedCandidate.toJSON = function toJSON() {\n return {\n candidate: augmentedCandidate.candidate,\n sdpMid: augmentedCandidate.sdpMid,\n sdpMLineIndex: augmentedCandidate.sdpMLineIndex,\n usernameFragment: augmentedCandidate.usernameFragment,\n };\n };\n return augmentedCandidate;\n }\n return new NativeRTCIceCandidate(args);\n };\n window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;\n\n // Hook up the augmented candidate in onicecandidate and\n // addEventListener('icecandidate', ...)\n utils.wrapPeerConnectionEvent(window, 'icecandidate', e => {\n if (e.candidate) {\n Object.defineProperty(e, 'candidate', {\n value: new window.RTCIceCandidate(e.candidate),\n writable: 'false'\n });\n }\n return e;\n });\n}\n\nexport function shimMaxMessageSize(window, browserDetails) {\n if (!window.RTCPeerConnection) {\n return;\n }\n\n if (!('sctp' in window.RTCPeerConnection.prototype)) {\n Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n }\n });\n }\n\n const sctpInDescription = function(description) {\n if (!description || !description.sdp) {\n return false;\n }\n const sections = SDPUtils.splitSections(description.sdp);\n sections.shift();\n return sections.some(mediaSection => {\n const mLine = SDPUtils.parseMLine(mediaSection);\n return mLine && mLine.kind === 'application'\n && mLine.protocol.indexOf('SCTP') !== -1;\n });\n };\n\n const getRemoteFirefoxVersion = function(description) {\n // TODO: Is there a better solution for detecting Firefox?\n const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\\d+)/);\n if (match === null || match.length < 2) {\n return -1;\n }\n const version = parseInt(match[1], 10);\n // Test for NaN (yes, this is ugly)\n return version !== version ? -1 : version;\n };\n\n const getCanSendMaxMessageSize = function(remoteIsFirefox) {\n // Every implementation we know can send at least 64 KiB.\n // Note: Although Chrome is technically able to send up to 256 KiB, the\n // data does not reach the other peer reliably.\n // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419\n let canSendMaxMessageSize = 65536;\n if (browserDetails.browser === 'firefox') {\n if (browserDetails.version < 57) {\n if (remoteIsFirefox === -1) {\n // FF < 57 will send in 16 KiB chunks using the deprecated PPID\n // fragmentation.\n canSendMaxMessageSize = 16384;\n } else {\n // However, other FF (and RAWRTC) can reassemble PPID-fragmented\n // messages. Thus, supporting ~2 GiB when sending.\n canSendMaxMessageSize = 2147483637;\n }\n } else if (browserDetails.version < 60) {\n // Currently, all FF >= 57 will reset the remote maximum message size\n // to the default value when a data channel is created at a later\n // stage. :(\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n canSendMaxMessageSize =\n browserDetails.version === 57 ? 65535 : 65536;\n } else {\n // FF >= 60 supports sending ~2 GiB\n canSendMaxMessageSize = 2147483637;\n }\n }\n return canSendMaxMessageSize;\n };\n\n const getMaxMessageSize = function(description, remoteIsFirefox) {\n // Note: 65536 bytes is the default value from the SDP spec. Also,\n // every implementation we know supports receiving 65536 bytes.\n let maxMessageSize = 65536;\n\n // FF 57 has a slightly incorrect default remote max message size, so\n // we need to adjust it here to avoid a failure when sending.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697\n if (browserDetails.browser === 'firefox'\n && browserDetails.version === 57) {\n maxMessageSize = 65535;\n }\n\n const match = SDPUtils.matchPrefix(description.sdp,\n 'a=max-message-size:');\n if (match.length > 0) {\n maxMessageSize = parseInt(match[0].substr(19), 10);\n } else if (browserDetails.browser === 'firefox' &&\n remoteIsFirefox !== -1) {\n // If the maximum message size is not present in the remote SDP and\n // both local and remote are Firefox, the remote peer can receive\n // ~2 GiB.\n maxMessageSize = 2147483637;\n }\n return maxMessageSize;\n };\n\n const origSetRemoteDescription =\n window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription() {\n this._sctp = null;\n // Chrome decided to not expose .sctp in plan-b mode.\n // As usual, adapter.js has to do an 'ugly worakaround'\n // to cover up the mess.\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {\n const {sdpSemantics} = this.getConfiguration();\n if (sdpSemantics === 'plan-b') {\n Object.defineProperty(this, 'sctp', {\n get() {\n return typeof this._sctp === 'undefined' ? null : this._sctp;\n },\n enumerable: true,\n configurable: true,\n });\n }\n }\n\n if (sctpInDescription(arguments[0])) {\n // Check if the remote is FF.\n const isFirefox = getRemoteFirefoxVersion(arguments[0]);\n\n // Get the maximum message size the local peer is capable of sending\n const canSendMMS = getCanSendMaxMessageSize(isFirefox);\n\n // Get the maximum message size of the remote peer.\n const remoteMMS = getMaxMessageSize(arguments[0], isFirefox);\n\n // Determine final maximum message size\n let maxMessageSize;\n if (canSendMMS === 0 && remoteMMS === 0) {\n maxMessageSize = Number.POSITIVE_INFINITY;\n } else if (canSendMMS === 0 || remoteMMS === 0) {\n maxMessageSize = Math.max(canSendMMS, remoteMMS);\n } else {\n maxMessageSize = Math.min(canSendMMS, remoteMMS);\n }\n\n // Create a dummy RTCSctpTransport object and the 'maxMessageSize'\n // attribute.\n const sctp = {};\n Object.defineProperty(sctp, 'maxMessageSize', {\n get() {\n return maxMessageSize;\n }\n });\n this._sctp = sctp;\n }\n\n return origSetRemoteDescription.apply(this, arguments);\n };\n}\n\nexport function shimSendThrowTypeError(window) {\n if (!(window.RTCPeerConnection &&\n 'createDataChannel' in window.RTCPeerConnection.prototype)) {\n return;\n }\n\n // Note: Although Firefox >= 57 has a native implementation, the maximum\n // message size can be reset for all data channels at a later stage.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n\n function wrapDcSend(dc, pc) {\n const origDataChannelSend = dc.send;\n dc.send = function send() {\n const data = arguments[0];\n const length = data.length || data.size || data.byteLength;\n if (dc.readyState === 'open' &&\n pc.sctp && length > pc.sctp.maxMessageSize) {\n throw new TypeError('Message too large (can send a maximum of ' +\n pc.sctp.maxMessageSize + ' bytes)');\n }\n return origDataChannelSend.apply(dc, arguments);\n };\n }\n const origCreateDataChannel =\n window.RTCPeerConnection.prototype.createDataChannel;\n window.RTCPeerConnection.prototype.createDataChannel =\n function createDataChannel() {\n const dataChannel = origCreateDataChannel.apply(this, arguments);\n wrapDcSend(dataChannel, this);\n return dataChannel;\n };\n utils.wrapPeerConnectionEvent(window, 'datachannel', e => {\n wrapDcSend(e.channel, e.target);\n return e;\n });\n}\n\n\n/* shims RTCConnectionState by pretending it is the same as iceConnectionState.\n * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12\n * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect\n * since DTLS failures would be hidden. See\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827\n * for the Firefox tracking bug.\n */\nexport function shimConnectionState(window) {\n if (!window.RTCPeerConnection ||\n 'connectionState' in window.RTCPeerConnection.prototype) {\n return;\n }\n const proto = window.RTCPeerConnection.prototype;\n Object.defineProperty(proto, 'connectionState', {\n get() {\n return {\n completed: 'connected',\n checking: 'connecting'\n }[this.iceConnectionState] || this.iceConnectionState;\n },\n enumerable: true,\n configurable: true\n });\n Object.defineProperty(proto, 'onconnectionstatechange', {\n get() {\n return this._onconnectionstatechange || null;\n },\n set(cb) {\n if (this._onconnectionstatechange) {\n this.removeEventListener('connectionstatechange',\n this._onconnectionstatechange);\n delete this._onconnectionstatechange;\n }\n if (cb) {\n this.addEventListener('connectionstatechange',\n this._onconnectionstatechange = cb);\n }\n },\n enumerable: true,\n configurable: true\n });\n\n ['setLocalDescription', 'setRemoteDescription'].forEach((method) => {\n const origMethod = proto[method];\n proto[method] = function() {\n if (!this._connectionstatechangepoly) {\n this._connectionstatechangepoly = e => {\n const pc = e.target;\n if (pc._lastConnectionState !== pc.connectionState) {\n pc._lastConnectionState = pc.connectionState;\n const newEvent = new Event('connectionstatechange', e);\n pc.dispatchEvent(newEvent);\n }\n return e;\n };\n this.addEventListener('iceconnectionstatechange',\n this._connectionstatechangepoly);\n }\n return origMethod.apply(this, arguments);\n };\n });\n}\n\nexport function removeExtmapAllowMixed(window, browserDetails) {\n /* remove a=extmap-allow-mixed for webrtc.org < M71 */\n if (!window.RTCPeerConnection) {\n return;\n }\n if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {\n return;\n }\n if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {\n return;\n }\n const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;\n window.RTCPeerConnection.prototype.setRemoteDescription =\n function setRemoteDescription(desc) {\n if (desc && desc.sdp && desc.sdp.indexOf('\\na=extmap-allow-mixed') !== -1) {\n const sdp = desc.sdp.split('\\n').filter((line) => {\n return line.trim() !== 'a=extmap-allow-mixed';\n }).join('\\n');\n // Safari enforces read-only-ness of RTCSessionDescription fields.\n if (window.RTCSessionDescription &&\n desc instanceof window.RTCSessionDescription) {\n arguments[0] = new window.RTCSessionDescription({\n type: desc.type,\n sdp,\n });\n } else {\n desc.sdp = sdp;\n }\n }\n return nativeSRD.apply(this, arguments);\n };\n}\n\nexport function shimAddIceCandidateNullOrEmpty(window, browserDetails) {\n // Support for addIceCandidate(null or undefined)\n // as well as addIceCandidate({candidate: \"\", ...})\n // https://bugs.chromium.org/p/chromium/issues/detail?id=978582\n // Note: must be called before other polyfills which change the signature.\n if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n return;\n }\n const nativeAddIceCandidate =\n window.RTCPeerConnection.prototype.addIceCandidate;\n if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {\n return;\n }\n window.RTCPeerConnection.prototype.addIceCandidate =\n function addIceCandidate() {\n if (!arguments[0]) {\n if (arguments[1]) {\n arguments[1].apply(null);\n }\n return Promise.resolve();\n }\n // Firefox 68+ emits and processes {candidate: \"\", ...}, ignore\n // in older versions.\n // Native support for ignoring exists for Chrome M77+.\n // Safari ignores as well, exact version unknown but works in the same\n // version that also ignores addIceCandidate(null).\n if (((browserDetails.browser === 'chrome' && browserDetails.version < 78)\n || (browserDetails.browser === 'firefox'\n && browserDetails.version < 68)\n || (browserDetails.browser === 'safari'))\n && arguments[0] && arguments[0].candidate === '') {\n return Promise.resolve();\n }\n return nativeAddIceCandidate.apply(this, arguments);\n };\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\nimport * as utils from './utils';\n\n // Browser shims.\nimport * as chromeShim from './chrome/chrome_shim';\nimport * as edgeShim from './edge/edge_shim';\nimport * as firefoxShim from './firefox/firefox_shim';\nimport * as safariShim from './safari/safari_shim';\nimport * as commonShim from './common_shim';\n\n// Shimming starts here.\nexport function adapterFactory({window} = {}, options = {\n shimChrome: true,\n shimFirefox: true,\n shimEdge: true,\n shimSafari: true,\n}) {\n // Utils.\n const logging = utils.log;\n const browserDetails = utils.detectBrowser(window);\n\n const adapter = {\n browserDetails,\n commonShim,\n extractVersion: utils.extractVersion,\n disableLog: utils.disableLog,\n disableWarnings: utils.disableWarnings\n };\n\n // Shim browser if found.\n switch (browserDetails.browser) {\n case 'chrome':\n if (!chromeShim || !chromeShim.shimPeerConnection ||\n !options.shimChrome) {\n logging('Chrome shim is not included in this adapter release.');\n return adapter;\n }\n if (browserDetails.version === null) {\n logging('Chrome shim can not determine version, not shimming.');\n return adapter;\n }\n logging('adapter.js shimming chrome.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = chromeShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n\n chromeShim.shimGetUserMedia(window, browserDetails);\n chromeShim.shimMediaStream(window, browserDetails);\n chromeShim.shimPeerConnection(window, browserDetails);\n chromeShim.shimOnTrack(window, browserDetails);\n chromeShim.shimAddTrackRemoveTrack(window, browserDetails);\n chromeShim.shimGetSendersWithDtmf(window, browserDetails);\n chromeShim.shimGetStats(window, browserDetails);\n chromeShim.shimSenderReceiverGetStats(window, browserDetails);\n chromeShim.fixNegotiationNeeded(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n case 'firefox':\n if (!firefoxShim || !firefoxShim.shimPeerConnection ||\n !options.shimFirefox) {\n logging('Firefox shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming firefox.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = firefoxShim;\n\n // Must be called before shimPeerConnection.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n\n firefoxShim.shimGetUserMedia(window, browserDetails);\n firefoxShim.shimPeerConnection(window, browserDetails);\n firefoxShim.shimOnTrack(window, browserDetails);\n firefoxShim.shimRemoveStream(window, browserDetails);\n firefoxShim.shimSenderGetStats(window, browserDetails);\n firefoxShim.shimReceiverGetStats(window, browserDetails);\n firefoxShim.shimRTCDataChannel(window, browserDetails);\n firefoxShim.shimAddTransceiver(window, browserDetails);\n firefoxShim.shimGetParameters(window, browserDetails);\n firefoxShim.shimCreateOffer(window, browserDetails);\n firefoxShim.shimCreateAnswer(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimConnectionState(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n break;\n case 'edge':\n if (!edgeShim || !edgeShim.shimPeerConnection || !options.shimEdge) {\n logging('MS edge shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming edge.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = edgeShim;\n\n edgeShim.shimGetUserMedia(window, browserDetails);\n edgeShim.shimGetDisplayMedia(window, browserDetails);\n edgeShim.shimPeerConnection(window, browserDetails);\n edgeShim.shimReplaceTrack(window, browserDetails);\n\n // the edge shim implements the full RTCIceCandidate object.\n\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n break;\n case 'safari':\n if (!safariShim || !options.shimSafari) {\n logging('Safari shim is not included in this adapter release.');\n return adapter;\n }\n logging('adapter.js shimming safari.');\n // Export to the adapter global object visible in the browser.\n adapter.browserShim = safariShim;\n\n // Must be called before shimCallbackAPI.\n commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n\n safariShim.shimRTCIceServerUrls(window, browserDetails);\n safariShim.shimCreateOfferLegacy(window, browserDetails);\n safariShim.shimCallbacksAPI(window, browserDetails);\n safariShim.shimLocalStreamsAPI(window, browserDetails);\n safariShim.shimRemoteStreamsAPI(window, browserDetails);\n safariShim.shimTrackEventTransceiver(window, browserDetails);\n safariShim.shimGetUserMedia(window, browserDetails);\n safariShim.shimAudioContext(window, browserDetails);\n\n commonShim.shimRTCIceCandidate(window, browserDetails);\n commonShim.shimMaxMessageSize(window, browserDetails);\n commonShim.shimSendThrowTypeError(window, browserDetails);\n commonShim.removeExtmapAllowMixed(window, browserDetails);\n break;\n default:\n logging('Unsupported browser!');\n break;\n }\n\n return adapter;\n}\n","/*\n * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n * Use of this source code is governed by a BSD-style license\n * that can be found in the LICENSE file in the root of the source\n * tree.\n */\n/* eslint-env node */\n\n'use strict';\n\nimport {adapterFactory} from './adapter_factory.js';\n\nconst adapter =\n adapterFactory({window: typeof window === 'undefined' ? undefined : window});\nexport default adapter;\n","\n'use strict';\nimport * as utils from './utils.js';\nimport * as MediaFormatModule from './mediaformat.js';\nimport adapter from 'webrtc-adapter';\n\n/**\n * @class AudioTrackConstraints\n * @classDesc Constraints for creating an audio MediaStreamTrack.\n * @memberof Owt.Base\n * @constructor\n * @param {Owt.Base.AudioSourceInfo} source Source info of this audio track.\n */\nexport class AudioTrackConstraints {\n // eslint-disable-next-line require-jsdoc\n constructor(source) {\n if (!Object.values(MediaFormatModule.AudioSourceInfo)\n .some((v) => v === source)) {\n throw new TypeError('Invalid source.');\n }\n /**\n * @member {string} source\n * @memberof Owt.Base.AudioTrackConstraints\n * @desc Values could be \"mic\", \"screen-cast\", \"file\" or \"mixed\".\n * @instance\n */\n this.source = source;\n /**\n * @member {string} deviceId\n * @memberof Owt.Base.AudioTrackConstraints\n * @desc Do not provide deviceId if source is not \"mic\".\n * @instance\n * @see https://w3c.github.io/mediacapture-main/#def-constraint-deviceId\n */\n this.deviceId = undefined;\n }\n}\n\n/**\n * @class VideoTrackConstraints\n * @classDesc Constraints for creating a video MediaStreamTrack.\n * @memberof Owt.Base\n * @constructor\n * @param {Owt.Base.VideoSourceInfo} source Source info of this video track.\n */\nexport class VideoTrackConstraints {\n // eslint-disable-next-line require-jsdoc\n constructor(source) {\n if (!Object.values(MediaFormatModule.VideoSourceInfo)\n .some((v) => v === source)) {\n throw new TypeError('Invalid source.');\n }\n /**\n * @member {string} source\n * @memberof Owt.Base.VideoTrackConstraints\n * @desc Values could be \"camera\", \"screen-cast\", \"file\" or \"mixed\".\n * @instance\n */\n this.source = source;\n /**\n * @member {string} deviceId\n * @memberof Owt.Base.VideoTrackConstraints\n * @desc Do not provide deviceId if source is not \"camera\".\n * @instance\n * @see https://w3c.github.io/mediacapture-main/#def-constraint-deviceId\n */\n\n this.deviceId = undefined;\n\n /**\n * @member {Owt.Base.Resolution} resolution\n * @memberof Owt.Base.VideoTrackConstraints\n * @instance\n */\n this.resolution = undefined;\n\n /**\n * @member {number} frameRate\n * @memberof Owt.Base.VideoTrackConstraints\n * @instance\n */\n this.frameRate = undefined;\n }\n}\n/**\n * @class StreamConstraints\n * @classDesc Constraints for creating a MediaStream from screen mic and camera.\n * @memberof Owt.Base\n * @constructor\n * @param {?Owt.Base.AudioTrackConstraints} audioConstraints\n * @param {?Owt.Base.VideoTrackConstraints} videoConstraints\n */\nexport class StreamConstraints {\n // eslint-disable-next-line require-jsdoc\n constructor(audioConstraints = false, videoConstraints = false) {\n /**\n * @member {Owt.Base.MediaStreamTrackDeviceConstraintsForAudio} audio\n * @memberof Owt.Base.MediaStreamDeviceConstraints\n * @instance\n */\n this.audio = audioConstraints;\n /**\n * @member {Owt.Base.MediaStreamTrackDeviceConstraintsForVideo} Video\n * @memberof Owt.Base.MediaStreamDeviceConstraints\n * @instance\n */\n this.video = videoConstraints;\n }\n}\n\n// eslint-disable-next-line require-jsdoc\nfunction isVideoConstrainsForScreenCast(constraints) {\n return (typeof constraints.video === 'object' && constraints.video.source ===\n MediaFormatModule.VideoSourceInfo.SCREENCAST);\n}\n\n/**\n * @class MediaStreamFactory\n * @classDesc A factory to create MediaStream. You can also create MediaStream by yourself.\n * @memberof Owt.Base\n */\nexport class MediaStreamFactory {\n /**\n * @function createMediaStream\n * @static\n * @desc Create a MediaStream with given constraints. If you want to create a MediaStream for screen cast, please make sure both audio and video's source are \"screen-cast\".\n * @memberof Owt.Base.MediaStreamFactory\n * @return {Promise} Return a promise that is resolved when stream is successfully created, or rejected if one of the following error happened:\n * - One or more parameters cannot be satisfied.\n * - Specified device is busy.\n * - Cannot obtain necessary permission or operation is canceled by user.\n * - Video source is screen cast, while audio source is not.\n * - Audio source is screen cast, while video source is disabled.\n * @param {Owt.Base.StreamConstraints} constraints\n */\n static createMediaStream(constraints) {\n if (typeof constraints !== 'object' ||\n (!constraints.audio && !constraints.video)) {\n return Promise.reject(new TypeError('Invalid constrains'));\n }\n if (!isVideoConstrainsForScreenCast(constraints) &&\n (typeof constraints.audio === 'object') &&\n constraints.audio.source ===\n MediaFormatModule.AudioSourceInfo.SCREENCAST) {\n return Promise.reject(\n new TypeError('Cannot share screen without video.'));\n }\n if (isVideoConstrainsForScreenCast(constraints) && !utils.isChrome() &&\n !utils.isFirefox()) {\n return Promise.reject(\n new TypeError('Screen sharing only supports Chrome and Firefox.'));\n }\n if (isVideoConstrainsForScreenCast(constraints) &&\n typeof constraints.audio === 'object' &&\n constraints.audio.source !==\n MediaFormatModule.AudioSourceInfo.SCREENCAST) {\n return Promise.reject(new TypeError(\n 'Cannot capture video from screen cast while capture audio from'\n + ' other source.'));\n }\n\n // Check and convert constraints.\n if (!constraints.audio && !constraints.video) {\n return Promise.reject(new TypeError(\n 'At least one of audio and video must be requested.'));\n }\n const mediaConstraints = Object.create({});\n if (typeof constraints.audio === 'object' &&\n constraints.audio.source === MediaFormatModule.AudioSourceInfo.MIC) {\n mediaConstraints.audio = Object.create({});\n if (utils.isEdge()) {\n mediaConstraints.audio.deviceId = constraints.audio.deviceId;\n } else {\n mediaConstraints.audio.deviceId = {\n exact: constraints.audio.deviceId,\n };\n }\n } else {\n if (constraints.audio.source ===\n MediaFormatModule.AudioSourceInfo.SCREENCAST) {\n mediaConstraints.audio = true;\n } else {\n mediaConstraints.audio = constraints.audio;\n }\n }\n if (typeof constraints.video === 'object') {\n mediaConstraints.video = Object.create({});\n if (typeof constraints.video.frameRate === 'number') {\n mediaConstraints.video.frameRate = constraints.video.frameRate;\n }\n if (constraints.video.resolution &&\n constraints.video.resolution.width &&\n constraints.video.resolution.height) {\n if (constraints.video.source ===\n MediaFormatModule.VideoSourceInfo.SCREENCAST) {\n mediaConstraints.video.width = constraints.video.resolution.width;\n mediaConstraints.video.height = constraints.video.resolution.height;\n } else {\n mediaConstraints.video.width = Object.create({});\n mediaConstraints.video.width.exact =\n constraints.video.resolution.width;\n mediaConstraints.video.height = Object.create({});\n mediaConstraints.video.height.exact =\n constraints.video.resolution.height;\n }\n }\n if (typeof constraints.video.deviceId === 'string') {\n mediaConstraints.video.deviceId = {exact: constraints.video.deviceId};\n }\n if (utils.isFirefox() &&\n constraints.video.source ===\n MediaFormatModule.VideoSourceInfo.SCREENCAST) {\n mediaConstraints.video.mediaSource = 'screen';\n }\n } else {\n mediaConstraints.video = constraints.video;\n }\n\n if (isVideoConstrainsForScreenCast(constraints)) {\n return navigator.mediaDevices.getDisplayMedia(mediaConstraints);\n } else {\n return navigator.mediaDevices.getUserMedia(mediaConstraints);\n }\n }\n}\n","// Copyright (C) <2018> Intel Corporation\n//\n// SPDX-License-Identifier: Apache-2.0\n\n'use strict';\n\nexport * from './mediastream-factory.js';\nexport * from './mediaformat.js';","let logger;\nlet errorLogger;\n\nexport function setLogger() {\n /*eslint-disable */\n logger = console.log;\n errorLogger = console.error;\n /*eslint-enable */\n}\n\nexport function isEnable() {\n return logger != null;\n}\n\nexport function log(message, ...optionalParams) {\n if (logger) {\n logger(message, ...optionalParams);\n }\n}\nexport function error(message, ...optionalParams) {\n if (errorLogger) {\n errorLogger(message, ...optionalParams);\n }\n}\n","export default class Event {\n constructor(type) {\n this.listener = {};\n this.type = type | '';\n }\n\n on(event, fn) {\n if (!this.listener[event]) {\n this.listener[event] = [];\n }\n this.listener[event].push(fn);\n return true;\n }\n\n off(event, fn) {\n if (this.listener[event]) {\n var index = this.listener[event].indexOf(fn);\n if (index > -1) {\n this.listener[event].splice(index, 1);\n }\n return true;\n }\n return false;\n }\n\n offAll() {\n this.listener = {};\n }\n\n dispatch(event, data) {\n if (this.listener[event]) {\n this.listener[event].map((each) => {\n each.apply(null, [data]);\n });\n return true;\n }\n return false;\n }\n}\n","'use strict';\n\nmodule.exports = function bind(fn, thisArg) {\n return function wrap() {\n var args = new Array(arguments.length);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i];\n }\n return fn.apply(thisArg, args);\n };\n};\n","'use strict';\n\nvar bind = require('./helpers/bind');\n\n/*global toString:true*/\n\n// utils is a library of generic helper functions non-specific to axios\n\nvar toString = Object.prototype.toString;\n\n/**\n * Determine if a value is an Array\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is an Array, otherwise false\n */\nfunction isArray(val) {\n return toString.call(val) === '[object Array]';\n}\n\n/**\n * Determine if a value is undefined\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if the value is undefined, otherwise false\n */\nfunction isUndefined(val) {\n return typeof val === 'undefined';\n}\n\n/**\n * Determine if a value is a Buffer\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Buffer, otherwise false\n */\nfunction isBuffer(val) {\n return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)\n && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);\n}\n\n/**\n * Determine if a value is an ArrayBuffer\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is an ArrayBuffer, otherwise false\n */\nfunction isArrayBuffer(val) {\n return toString.call(val) === '[object ArrayBuffer]';\n}\n\n/**\n * Determine if a value is a FormData\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is an FormData, otherwise false\n */\nfunction isFormData(val) {\n return (typeof FormData !== 'undefined') && (val instanceof FormData);\n}\n\n/**\n * Determine if a value is a view on an ArrayBuffer\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false\n */\nfunction isArrayBufferView(val) {\n var result;\n if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {\n result = ArrayBuffer.isView(val);\n } else {\n result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer);\n }\n return result;\n}\n\n/**\n * Determine if a value is a String\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a String, otherwise false\n */\nfunction isString(val) {\n return typeof val === 'string';\n}\n\n/**\n * Determine if a value is a Number\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Number, otherwise false\n */\nfunction isNumber(val) {\n return typeof val === 'number';\n}\n\n/**\n * Determine if a value is an Object\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is an Object, otherwise false\n */\nfunction isObject(val) {\n return val !== null && typeof val === 'object';\n}\n\n/**\n * Determine if a value is a plain Object\n *\n * @param {Object} val The value to test\n * @return {boolean} True if value is a plain Object, otherwise false\n */\nfunction isPlainObject(val) {\n if (toString.call(val) !== '[object Object]') {\n return false;\n }\n\n var prototype = Object.getPrototypeOf(val);\n return prototype === null || prototype === Object.prototype;\n}\n\n/**\n * Determine if a value is a Date\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Date, otherwise false\n */\nfunction isDate(val) {\n return toString.call(val) === '[object Date]';\n}\n\n/**\n * Determine if a value is a File\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a File, otherwise false\n */\nfunction isFile(val) {\n return toString.call(val) === '[object File]';\n}\n\n/**\n * Determine if a value is a Blob\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Blob, otherwise false\n */\nfunction isBlob(val) {\n return toString.call(val) === '[object Blob]';\n}\n\n/**\n * Determine if a value is a Function\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Function, otherwise false\n */\nfunction isFunction(val) {\n return toString.call(val) === '[object Function]';\n}\n\n/**\n * Determine if a value is a Stream\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Stream, otherwise false\n */\nfunction isStream(val) {\n return isObject(val) && isFunction(val.pipe);\n}\n\n/**\n * Determine if a value is a URLSearchParams object\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a URLSearchParams object, otherwise false\n */\nfunction isURLSearchParams(val) {\n return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams;\n}\n\n/**\n * Trim excess whitespace off the beginning and end of a string\n *\n * @param {String} str The String to trim\n * @returns {String} The String freed of excess whitespace\n */\nfunction trim(str) {\n return str.replace(/^\\s*/, '').replace(/\\s*$/, '');\n}\n\n/**\n * Determine if we're running in a standard browser environment\n *\n * This allows axios to run in a web worker, and react-native.\n * Both environments support XMLHttpRequest, but not fully standard globals.\n *\n * web workers:\n * typeof window -> undefined\n * typeof document -> undefined\n *\n * react-native:\n * navigator.product -> 'ReactNative'\n * nativescript\n * navigator.product -> 'NativeScript' or 'NS'\n */\nfunction isStandardBrowserEnv() {\n if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||\n navigator.product === 'NativeScript' ||\n navigator.product === 'NS')) {\n return false;\n }\n return (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined'\n );\n}\n\n/**\n * Iterate over an Array or an Object invoking a function for each item.\n *\n * If `obj` is an Array callback will be called passing\n * the value, index, and complete array for each item.\n *\n * If 'obj' is an Object callback will be called passing\n * the value, key, and complete object for each property.\n *\n * @param {Object|Array} obj The object to iterate\n * @param {Function} fn The callback to invoke for each item\n */\nfunction forEach(obj, fn) {\n // Don't bother if no value provided\n if (obj === null || typeof obj === 'undefined') {\n return;\n }\n\n // Force an array if not already something iterable\n if (typeof obj !== 'object') {\n /*eslint no-param-reassign:0*/\n obj = [obj];\n }\n\n if (isArray(obj)) {\n // Iterate over array values\n for (var i = 0, l = obj.length; i < l; i++) {\n fn.call(null, obj[i], i, obj);\n }\n } else {\n // Iterate over object keys\n for (var key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n fn.call(null, obj[key], key, obj);\n }\n }\n }\n}\n\n/**\n * Accepts varargs expecting each argument to be an object, then\n * immutably merges the properties of each object and returns result.\n *\n * When multiple objects contain the same key the later object in\n * the arguments list will take precedence.\n *\n * Example:\n *\n * ```js\n * var result = merge({foo: 123}, {foo: 456});\n * console.log(result.foo); // outputs 456\n * ```\n *\n * @param {Object} obj1 Object to merge\n * @returns {Object} Result of all merge properties\n */\nfunction merge(/* obj1, obj2, obj3, ... */) {\n var result = {};\n function assignValue(val, key) {\n if (isPlainObject(result[key]) && isPlainObject(val)) {\n result[key] = merge(result[key], val);\n } else if (isPlainObject(val)) {\n result[key] = merge({}, val);\n } else if (isArray(val)) {\n result[key] = val.slice();\n } else {\n result[key] = val;\n }\n }\n\n for (var i = 0, l = arguments.length; i < l; i++) {\n forEach(arguments[i], assignValue);\n }\n return result;\n}\n\n/**\n * Extends object a by mutably adding to it the properties of object b.\n *\n * @param {Object} a The object to be extended\n * @param {Object} b The object to copy properties from\n * @param {Object} thisArg The object to bind function to\n * @return {Object} The resulting value of object a\n */\nfunction extend(a, b, thisArg) {\n forEach(b, function assignValue(val, key) {\n if (thisArg && typeof val === 'function') {\n a[key] = bind(val, thisArg);\n } else {\n a[key] = val;\n }\n });\n return a;\n}\n\n/**\n * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)\n *\n * @param {string} content with BOM\n * @return {string} content value without BOM\n */\nfunction stripBOM(content) {\n if (content.charCodeAt(0) === 0xFEFF) {\n content = content.slice(1);\n }\n return content;\n}\n\nmodule.exports = {\n isArray: isArray,\n isArrayBuffer: isArrayBuffer,\n isBuffer: isBuffer,\n isFormData: isFormData,\n isArrayBufferView: isArrayBufferView,\n isString: isString,\n isNumber: isNumber,\n isObject: isObject,\n isPlainObject: isPlainObject,\n isUndefined: isUndefined,\n isDate: isDate,\n isFile: isFile,\n isBlob: isBlob,\n isFunction: isFunction,\n isStream: isStream,\n isURLSearchParams: isURLSearchParams,\n isStandardBrowserEnv: isStandardBrowserEnv,\n forEach: forEach,\n merge: merge,\n extend: extend,\n trim: trim,\n stripBOM: stripBOM\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction encode(val) {\n return encodeURIComponent(val).\n replace(/%3A/gi, ':').\n replace(/%24/g, '$').\n replace(/%2C/gi, ',').\n replace(/%20/g, '+').\n replace(/%5B/gi, '[').\n replace(/%5D/gi, ']');\n}\n\n/**\n * Build a URL by appending params to the end\n *\n * @param {string} url The base of the url (e.g., http://www.google.com)\n * @param {object} [params] The params to be appended\n * @returns {string} The formatted url\n */\nmodule.exports = function buildURL(url, params, paramsSerializer) {\n /*eslint no-param-reassign:0*/\n if (!params) {\n return url;\n }\n\n var serializedParams;\n if (paramsSerializer) {\n serializedParams = paramsSerializer(params);\n } else if (utils.isURLSearchParams(params)) {\n serializedParams = params.toString();\n } else {\n var parts = [];\n\n utils.forEach(params, function serialize(val, key) {\n if (val === null || typeof val === 'undefined') {\n return;\n }\n\n if (utils.isArray(val)) {\n key = key + '[]';\n } else {\n val = [val];\n }\n\n utils.forEach(val, function parseValue(v) {\n if (utils.isDate(v)) {\n v = v.toISOString();\n } else if (utils.isObject(v)) {\n v = JSON.stringify(v);\n }\n parts.push(encode(key) + '=' + encode(v));\n });\n });\n\n serializedParams = parts.join('&');\n }\n\n if (serializedParams) {\n var hashmarkIndex = url.indexOf('#');\n if (hashmarkIndex !== -1) {\n url = url.slice(0, hashmarkIndex);\n }\n\n url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;\n }\n\n return url;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction InterceptorManager() {\n this.handlers = [];\n}\n\n/**\n * Add a new interceptor to the stack\n *\n * @param {Function} fulfilled The function to handle `then` for a `Promise`\n * @param {Function} rejected The function to handle `reject` for a `Promise`\n *\n * @return {Number} An ID used to remove interceptor later\n */\nInterceptorManager.prototype.use = function use(fulfilled, rejected) {\n this.handlers.push({\n fulfilled: fulfilled,\n rejected: rejected\n });\n return this.handlers.length - 1;\n};\n\n/**\n * Remove an interceptor from the stack\n *\n * @param {Number} id The ID that was returned by `use`\n */\nInterceptorManager.prototype.eject = function eject(id) {\n if (this.handlers[id]) {\n this.handlers[id] = null;\n }\n};\n\n/**\n * Iterate over all the registered interceptors\n *\n * This method is particularly useful for skipping over any\n * interceptors that may have become `null` calling `eject`.\n *\n * @param {Function} fn The function to call for each interceptor\n */\nInterceptorManager.prototype.forEach = function forEach(fn) {\n utils.forEach(this.handlers, function forEachHandler(h) {\n if (h !== null) {\n fn(h);\n }\n });\n};\n\nmodule.exports = InterceptorManager;\n","'use strict';\n\nvar utils = require('./../utils');\n\n/**\n * Transform the data for a request or a response\n *\n * @param {Object|String} data The data to be transformed\n * @param {Array} headers The headers for the request or response\n * @param {Array|Function} fns A single function or Array of functions\n * @returns {*} The resulting transformed data\n */\nmodule.exports = function transformData(data, headers, fns) {\n /*eslint no-param-reassign:0*/\n utils.forEach(fns, function transform(fn) {\n data = fn(data, headers);\n });\n\n return data;\n};\n","'use strict';\n\nmodule.exports = function isCancel(value) {\n return !!(value && value.__CANCEL__);\n};\n","'use strict';\n\nvar utils = require('../utils');\n\nmodule.exports = function normalizeHeaderName(headers, normalizedName) {\n utils.forEach(headers, function processHeader(value, name) {\n if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {\n headers[normalizedName] = value;\n delete headers[name];\n }\n });\n};\n","'use strict';\n\n/**\n * Update an Error with the specified config, error code, and response.\n *\n * @param {Error} error The error to update.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The error.\n */\nmodule.exports = function enhanceError(error, config, code, request, response) {\n error.config = config;\n if (code) {\n error.code = code;\n }\n\n error.request = request;\n error.response = response;\n error.isAxiosError = true;\n\n error.toJSON = function toJSON() {\n return {\n // Standard\n message: this.message,\n name: this.name,\n // Microsoft\n description: this.description,\n number: this.number,\n // Mozilla\n fileName: this.fileName,\n lineNumber: this.lineNumber,\n columnNumber: this.columnNumber,\n stack: this.stack,\n // Axios\n config: this.config,\n code: this.code\n };\n };\n return error;\n};\n","'use strict';\n\nvar enhanceError = require('./enhanceError');\n\n/**\n * Create an Error with the specified message, config, error code, request and response.\n *\n * @param {string} message The error message.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The created error.\n */\nmodule.exports = function createError(message, config, code, request, response) {\n var error = new Error(message);\n return enhanceError(error, config, code, request, response);\n};\n","'use strict';\n\nvar createError = require('./createError');\n\n/**\n * Resolve or reject a Promise based on response status.\n *\n * @param {Function} resolve A function that resolves the promise.\n * @param {Function} reject A function that rejects the promise.\n * @param {object} response The response.\n */\nmodule.exports = function settle(resolve, reject, response) {\n var validateStatus = response.config.validateStatus;\n if (!response.status || !validateStatus || validateStatus(response.status)) {\n resolve(response);\n } else {\n reject(createError(\n 'Request failed with status code ' + response.status,\n response.config,\n null,\n response.request,\n response\n ));\n }\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs support document.cookie\n (function standardBrowserEnv() {\n return {\n write: function write(name, value, expires, path, domain, secure) {\n var cookie = [];\n cookie.push(name + '=' + encodeURIComponent(value));\n\n if (utils.isNumber(expires)) {\n cookie.push('expires=' + new Date(expires).toGMTString());\n }\n\n if (utils.isString(path)) {\n cookie.push('path=' + path);\n }\n\n if (utils.isString(domain)) {\n cookie.push('domain=' + domain);\n }\n\n if (secure === true) {\n cookie.push('secure');\n }\n\n document.cookie = cookie.join('; ');\n },\n\n read: function read(name) {\n var match = document.cookie.match(new RegExp('(^|;\\\\s*)(' + name + ')=([^;]*)'));\n return (match ? decodeURIComponent(match[3]) : null);\n },\n\n remove: function remove(name) {\n this.write(name, '', Date.now() - 86400000);\n }\n };\n })() :\n\n // Non standard browser env (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return {\n write: function write() {},\n read: function read() { return null; },\n remove: function remove() {}\n };\n })()\n);\n","'use strict';\n\n/**\n * Determines whether the specified URL is absolute\n *\n * @param {string} url The URL to test\n * @returns {boolean} True if the specified URL is absolute, otherwise false\n */\nmodule.exports = function isAbsoluteURL(url) {\n // A URL is considered absolute if it begins with \"://\" or \"//\" (protocol-relative URL).\n // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed\n // by any combination of letters, digits, plus, period, or hyphen.\n return /^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(url);\n};\n","'use strict';\n\n/**\n * Creates a new URL by combining the specified URLs\n *\n * @param {string} baseURL The base URL\n * @param {string} relativeURL The relative URL\n * @returns {string} The combined URL\n */\nmodule.exports = function combineURLs(baseURL, relativeURL) {\n return relativeURL\n ? baseURL.replace(/\\/+$/, '') + '/' + relativeURL.replace(/^\\/+/, '')\n : baseURL;\n};\n","'use strict';\n\nvar isAbsoluteURL = require('../helpers/isAbsoluteURL');\nvar combineURLs = require('../helpers/combineURLs');\n\n/**\n * Creates a new URL by combining the baseURL with the requestedURL,\n * only when the requestedURL is not already an absolute URL.\n * If the requestURL is absolute, this function returns the requestedURL untouched.\n *\n * @param {string} baseURL The base URL\n * @param {string} requestedURL Absolute or relative URL to combine\n * @returns {string} The combined full path\n */\nmodule.exports = function buildFullPath(baseURL, requestedURL) {\n if (baseURL && !isAbsoluteURL(requestedURL)) {\n return combineURLs(baseURL, requestedURL);\n }\n return requestedURL;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\n// Headers whose duplicates are ignored by node\n// c.f. https://nodejs.org/api/http.html#http_message_headers\nvar ignoreDuplicateOf = [\n 'age', 'authorization', 'content-length', 'content-type', 'etag',\n 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',\n 'last-modified', 'location', 'max-forwards', 'proxy-authorization',\n 'referer', 'retry-after', 'user-agent'\n];\n\n/**\n * Parse headers into an object\n *\n * ```\n * Date: Wed, 27 Aug 2014 08:58:49 GMT\n * Content-Type: application/json\n * Connection: keep-alive\n * Transfer-Encoding: chunked\n * ```\n *\n * @param {String} headers Headers needing to be parsed\n * @returns {Object} Headers parsed into an object\n */\nmodule.exports = function parseHeaders(headers) {\n var parsed = {};\n var key;\n var val;\n var i;\n\n if (!headers) { return parsed; }\n\n utils.forEach(headers.split('\\n'), function parser(line) {\n i = line.indexOf(':');\n key = utils.trim(line.substr(0, i)).toLowerCase();\n val = utils.trim(line.substr(i + 1));\n\n if (key) {\n if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {\n return;\n }\n if (key === 'set-cookie') {\n parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);\n } else {\n parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;\n }\n }\n });\n\n return parsed;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs have full support of the APIs needed to test\n // whether the request URL is of the same origin as current location.\n (function standardBrowserEnv() {\n var msie = /(msie|trident)/i.test(navigator.userAgent);\n var urlParsingNode = document.createElement('a');\n var originURL;\n\n /**\n * Parse a URL to discover it's components\n *\n * @param {String} url The URL to be parsed\n * @returns {Object}\n */\n function resolveURL(url) {\n var href = url;\n\n if (msie) {\n // IE needs attribute set twice to normalize properties\n urlParsingNode.setAttribute('href', href);\n href = urlParsingNode.href;\n }\n\n urlParsingNode.setAttribute('href', href);\n\n // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils\n return {\n href: urlParsingNode.href,\n protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',\n host: urlParsingNode.host,\n search: urlParsingNode.search ? urlParsingNode.search.replace(/^\\?/, '') : '',\n hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',\n hostname: urlParsingNode.hostname,\n port: urlParsingNode.port,\n pathname: (urlParsingNode.pathname.charAt(0) === '/') ?\n urlParsingNode.pathname :\n '/' + urlParsingNode.pathname\n };\n }\n\n originURL = resolveURL(window.location.href);\n\n /**\n * Determine if a URL shares the same origin as the current location\n *\n * @param {String} requestURL The URL to test\n * @returns {boolean} True if URL shares the same origin, otherwise false\n */\n return function isURLSameOrigin(requestURL) {\n var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;\n return (parsed.protocol === originURL.protocol &&\n parsed.host === originURL.host);\n };\n })() :\n\n // Non standard browser envs (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return function isURLSameOrigin() {\n return true;\n };\n })()\n);\n","'use strict';\n\nvar utils = require('./../utils');\nvar settle = require('./../core/settle');\nvar cookies = require('./../helpers/cookies');\nvar buildURL = require('./../helpers/buildURL');\nvar buildFullPath = require('../core/buildFullPath');\nvar parseHeaders = require('./../helpers/parseHeaders');\nvar isURLSameOrigin = require('./../helpers/isURLSameOrigin');\nvar createError = require('../core/createError');\n\nmodule.exports = function xhrAdapter(config) {\n return new Promise(function dispatchXhrRequest(resolve, reject) {\n var requestData = config.data;\n var requestHeaders = config.headers;\n\n if (utils.isFormData(requestData)) {\n delete requestHeaders['Content-Type']; // Let the browser set it\n }\n\n var request = new XMLHttpRequest();\n\n // HTTP basic authentication\n if (config.auth) {\n var username = config.auth.username || '';\n var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';\n requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);\n }\n\n var fullPath = buildFullPath(config.baseURL, config.url);\n request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);\n\n // Set the request timeout in MS\n request.timeout = config.timeout;\n\n // Listen for ready state\n request.onreadystatechange = function handleLoad() {\n if (!request || request.readyState !== 4) {\n return;\n }\n\n // The request errored out and we didn't get a response, this will be\n // handled by onerror instead\n // With one exception: request that using file: protocol, most browsers\n // will return status as 0 even though it's a successful request\n if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {\n return;\n }\n\n // Prepare the response\n var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;\n var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;\n var response = {\n data: responseData,\n status: request.status,\n statusText: request.statusText,\n headers: responseHeaders,\n config: config,\n request: request\n };\n\n settle(resolve, reject, response);\n\n // Clean up request\n request = null;\n };\n\n // Handle browser request cancellation (as opposed to a manual cancellation)\n request.onabort = function handleAbort() {\n if (!request) {\n return;\n }\n\n reject(createError('Request aborted', config, 'ECONNABORTED', request));\n\n // Clean up request\n request = null;\n };\n\n // Handle low level network errors\n request.onerror = function handleError() {\n // Real errors are hidden from us by the browser\n // onerror should only fire if it's a network error\n reject(createError('Network Error', config, null, request));\n\n // Clean up request\n request = null;\n };\n\n // Handle timeout\n request.ontimeout = function handleTimeout() {\n var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';\n if (config.timeoutErrorMessage) {\n timeoutErrorMessage = config.timeoutErrorMessage;\n }\n reject(createError(timeoutErrorMessage, config, 'ECONNABORTED',\n request));\n\n // Clean up request\n request = null;\n };\n\n // Add xsrf header\n // This is only done if running in a standard browser environment.\n // Specifically not if we're in a web worker, or react-native.\n if (utils.isStandardBrowserEnv()) {\n // Add xsrf header\n var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?\n cookies.read(config.xsrfCookieName) :\n undefined;\n\n if (xsrfValue) {\n requestHeaders[config.xsrfHeaderName] = xsrfValue;\n }\n }\n\n // Add headers to the request\n if ('setRequestHeader' in request) {\n utils.forEach(requestHeaders, function setRequestHeader(val, key) {\n if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {\n // Remove Content-Type if data is undefined\n delete requestHeaders[key];\n } else {\n // Otherwise add header to the request\n request.setRequestHeader(key, val);\n }\n });\n }\n\n // Add withCredentials to request if needed\n if (!utils.isUndefined(config.withCredentials)) {\n request.withCredentials = !!config.withCredentials;\n }\n\n // Add responseType to request if needed\n if (config.responseType) {\n try {\n request.responseType = config.responseType;\n } catch (e) {\n // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.\n // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.\n if (config.responseType !== 'json') {\n throw e;\n }\n }\n }\n\n // Handle progress if needed\n if (typeof config.onDownloadProgress === 'function') {\n request.addEventListener('progress', config.onDownloadProgress);\n }\n\n // Not all browsers support upload events\n if (typeof config.onUploadProgress === 'function' && request.upload) {\n request.upload.addEventListener('progress', config.onUploadProgress);\n }\n\n if (config.cancelToken) {\n // Handle cancellation\n config.cancelToken.promise.then(function onCanceled(cancel) {\n if (!request) {\n return;\n }\n\n request.abort();\n reject(cancel);\n // Clean up request\n request = null;\n });\n }\n\n if (!requestData) {\n requestData = null;\n }\n\n // Send the request\n request.send(requestData);\n });\n};\n","'use strict';\n\nvar utils = require('./utils');\nvar normalizeHeaderName = require('./helpers/normalizeHeaderName');\n\nvar DEFAULT_CONTENT_TYPE = {\n 'Content-Type': 'application/x-www-form-urlencoded'\n};\n\nfunction setContentTypeIfUnset(headers, value) {\n if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {\n headers['Content-Type'] = value;\n }\n}\n\nfunction getDefaultAdapter() {\n var adapter;\n if (typeof XMLHttpRequest !== 'undefined') {\n // For browsers use XHR adapter\n adapter = require('./adapters/xhr');\n } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {\n // For node use HTTP adapter\n adapter = require('./adapters/http');\n }\n return adapter;\n}\n\nvar defaults = {\n adapter: getDefaultAdapter(),\n\n transformRequest: [function transformRequest(data, headers) {\n normalizeHeaderName(headers, 'Accept');\n normalizeHeaderName(headers, 'Content-Type');\n if (utils.isFormData(data) ||\n utils.isArrayBuffer(data) ||\n utils.isBuffer(data) ||\n utils.isStream(data) ||\n utils.isFile(data) ||\n utils.isBlob(data)\n ) {\n return data;\n }\n if (utils.isArrayBufferView(data)) {\n return data.buffer;\n }\n if (utils.isURLSearchParams(data)) {\n setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');\n return data.toString();\n }\n if (utils.isObject(data)) {\n setContentTypeIfUnset(headers, 'application/json;charset=utf-8');\n return JSON.stringify(data);\n }\n return data;\n }],\n\n transformResponse: [function transformResponse(data) {\n /*eslint no-param-reassign:0*/\n if (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n } catch (e) { /* Ignore */ }\n }\n return data;\n }],\n\n /**\n * A timeout in milliseconds to abort a request. If set to 0 (default) a\n * timeout is not created.\n */\n timeout: 0,\n\n xsrfCookieName: 'XSRF-TOKEN',\n xsrfHeaderName: 'X-XSRF-TOKEN',\n\n maxContentLength: -1,\n maxBodyLength: -1,\n\n validateStatus: function validateStatus(status) {\n return status >= 200 && status < 300;\n }\n};\n\ndefaults.headers = {\n common: {\n 'Accept': 'application/json, text/plain, */*'\n }\n};\n\nutils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {\n defaults.headers[method] = {};\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);\n});\n\nmodule.exports = defaults;\n","'use strict';\n\nvar utils = require('./../utils');\nvar transformData = require('./transformData');\nvar isCancel = require('../cancel/isCancel');\nvar defaults = require('../defaults');\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nfunction throwIfCancellationRequested(config) {\n if (config.cancelToken) {\n config.cancelToken.throwIfRequested();\n }\n}\n\n/**\n * Dispatch a request to the server using the configured adapter.\n *\n * @param {object} config The config that is to be used for the request\n * @returns {Promise} The Promise to be fulfilled\n */\nmodule.exports = function dispatchRequest(config) {\n throwIfCancellationRequested(config);\n\n // Ensure headers exist\n config.headers = config.headers || {};\n\n // Transform request data\n config.data = transformData(\n config.data,\n config.headers,\n config.transformRequest\n );\n\n // Flatten headers\n config.headers = utils.merge(\n config.headers.common || {},\n config.headers[config.method] || {},\n config.headers\n );\n\n utils.forEach(\n ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],\n function cleanHeaderConfig(method) {\n delete config.headers[method];\n }\n );\n\n var adapter = config.adapter || defaults.adapter;\n\n return adapter(config).then(function onAdapterResolution(response) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n response.data = transformData(\n response.data,\n response.headers,\n config.transformResponse\n );\n\n return response;\n }, function onAdapterRejection(reason) {\n if (!isCancel(reason)) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n if (reason && reason.response) {\n reason.response.data = transformData(\n reason.response.data,\n reason.response.headers,\n config.transformResponse\n );\n }\n }\n\n return Promise.reject(reason);\n });\n};\n","'use strict';\n\nvar utils = require('../utils');\n\n/**\n * Config-specific merge-function which creates a new config-object\n * by merging two configuration objects together.\n *\n * @param {Object} config1\n * @param {Object} config2\n * @returns {Object} New object resulting from merging config2 to config1\n */\nmodule.exports = function mergeConfig(config1, config2) {\n // eslint-disable-next-line no-param-reassign\n config2 = config2 || {};\n var config = {};\n\n var valueFromConfig2Keys = ['url', 'method', 'data'];\n var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params'];\n var defaultToConfig2Keys = [\n 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',\n 'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',\n 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress',\n 'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent',\n 'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding'\n ];\n var directMergeKeys = ['validateStatus'];\n\n function getMergedValue(target, source) {\n if (utils.isPlainObject(target) && utils.isPlainObject(source)) {\n return utils.merge(target, source);\n } else if (utils.isPlainObject(source)) {\n return utils.merge({}, source);\n } else if (utils.isArray(source)) {\n return source.slice();\n }\n return source;\n }\n\n function mergeDeepProperties(prop) {\n if (!utils.isUndefined(config2[prop])) {\n config[prop] = getMergedValue(config1[prop], config2[prop]);\n } else if (!utils.isUndefined(config1[prop])) {\n config[prop] = getMergedValue(undefined, config1[prop]);\n }\n }\n\n utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {\n if (!utils.isUndefined(config2[prop])) {\n config[prop] = getMergedValue(undefined, config2[prop]);\n }\n });\n\n utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties);\n\n utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {\n if (!utils.isUndefined(config2[prop])) {\n config[prop] = getMergedValue(undefined, config2[prop]);\n } else if (!utils.isUndefined(config1[prop])) {\n config[prop] = getMergedValue(undefined, config1[prop]);\n }\n });\n\n utils.forEach(directMergeKeys, function merge(prop) {\n if (prop in config2) {\n config[prop] = getMergedValue(config1[prop], config2[prop]);\n } else if (prop in config1) {\n config[prop] = getMergedValue(undefined, config1[prop]);\n }\n });\n\n var axiosKeys = valueFromConfig2Keys\n .concat(mergeDeepPropertiesKeys)\n .concat(defaultToConfig2Keys)\n .concat(directMergeKeys);\n\n var otherKeys = Object\n .keys(config1)\n .concat(Object.keys(config2))\n .filter(function filterAxiosKeys(key) {\n return axiosKeys.indexOf(key) === -1;\n });\n\n utils.forEach(otherKeys, mergeDeepProperties);\n\n return config;\n};\n","'use strict';\n\nvar utils = require('./../utils');\nvar buildURL = require('../helpers/buildURL');\nvar InterceptorManager = require('./InterceptorManager');\nvar dispatchRequest = require('./dispatchRequest');\nvar mergeConfig = require('./mergeConfig');\n\n/**\n * Create a new instance of Axios\n *\n * @param {Object} instanceConfig The default config for the instance\n */\nfunction Axios(instanceConfig) {\n this.defaults = instanceConfig;\n this.interceptors = {\n request: new InterceptorManager(),\n response: new InterceptorManager()\n };\n}\n\n/**\n * Dispatch a request\n *\n * @param {Object} config The config specific for this request (merged with this.defaults)\n */\nAxios.prototype.request = function request(config) {\n /*eslint no-param-reassign:0*/\n // Allow for axios('example/url'[, config]) a la fetch API\n if (typeof config === 'string') {\n config = arguments[1] || {};\n config.url = arguments[0];\n } else {\n config = config || {};\n }\n\n config = mergeConfig(this.defaults, config);\n\n // Set config.method\n if (config.method) {\n config.method = config.method.toLowerCase();\n } else if (this.defaults.method) {\n config.method = this.defaults.method.toLowerCase();\n } else {\n config.method = 'get';\n }\n\n // Hook up interceptors middleware\n var chain = [dispatchRequest, undefined];\n var promise = Promise.resolve(config);\n\n this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {\n chain.unshift(interceptor.fulfilled, interceptor.rejected);\n });\n\n this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {\n chain.push(interceptor.fulfilled, interceptor.rejected);\n });\n\n while (chain.length) {\n promise = promise.then(chain.shift(), chain.shift());\n }\n\n return promise;\n};\n\nAxios.prototype.getUri = function getUri(config) {\n config = mergeConfig(this.defaults, config);\n return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\\?/, '');\n};\n\n// Provide aliases for supported request methods\nutils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, config) {\n return this.request(mergeConfig(config || {}, {\n method: method,\n url: url,\n data: (config || {}).data\n }));\n };\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, data, config) {\n return this.request(mergeConfig(config || {}, {\n method: method,\n url: url,\n data: data\n }));\n };\n});\n\nmodule.exports = Axios;\n","'use strict';\n\n/**\n * A `Cancel` is an object that is thrown when an operation is canceled.\n *\n * @class\n * @param {string=} message The message.\n */\nfunction Cancel(message) {\n this.message = message;\n}\n\nCancel.prototype.toString = function toString() {\n return 'Cancel' + (this.message ? ': ' + this.message : '');\n};\n\nCancel.prototype.__CANCEL__ = true;\n\nmodule.exports = Cancel;\n","'use strict';\n\nvar Cancel = require('./Cancel');\n\n/**\n * A `CancelToken` is an object that can be used to request cancellation of an operation.\n *\n * @class\n * @param {Function} executor The executor function.\n */\nfunction CancelToken(executor) {\n if (typeof executor !== 'function') {\n throw new TypeError('executor must be a function.');\n }\n\n var resolvePromise;\n this.promise = new Promise(function promiseExecutor(resolve) {\n resolvePromise = resolve;\n });\n\n var token = this;\n executor(function cancel(message) {\n if (token.reason) {\n // Cancellation has already been requested\n return;\n }\n\n token.reason = new Cancel(message);\n resolvePromise(token.reason);\n });\n}\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nCancelToken.prototype.throwIfRequested = function throwIfRequested() {\n if (this.reason) {\n throw this.reason;\n }\n};\n\n/**\n * Returns an object that contains a new `CancelToken` and a function that, when called,\n * cancels the `CancelToken`.\n */\nCancelToken.source = function source() {\n var cancel;\n var token = new CancelToken(function executor(c) {\n cancel = c;\n });\n return {\n token: token,\n cancel: cancel\n };\n};\n\nmodule.exports = CancelToken;\n","'use strict';\n\n/**\n * Syntactic sugar for invoking a function and expanding an array for arguments.\n *\n * Common use case would be to use `Function.prototype.apply`.\n *\n * ```js\n * function f(x, y, z) {}\n * var args = [1, 2, 3];\n * f.apply(null, args);\n * ```\n *\n * With `spread` this example can be re-written.\n *\n * ```js\n * spread(function(x, y, z) {})([1, 2, 3]);\n * ```\n *\n * @param {Function} callback\n * @returns {Function}\n */\nmodule.exports = function spread(callback) {\n return function wrap(arr) {\n return callback.apply(null, arr);\n };\n};\n","'use strict';\n\n/**\n * Determines whether the payload is an error thrown by Axios\n *\n * @param {*} payload The value to test\n * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false\n */\nmodule.exports = function isAxiosError(payload) {\n return (typeof payload === 'object') && (payload.isAxiosError === true);\n};\n","'use strict';\n\nvar utils = require('./utils');\nvar bind = require('./helpers/bind');\nvar Axios = require('./core/Axios');\nvar mergeConfig = require('./core/mergeConfig');\nvar defaults = require('./defaults');\n\n/**\n * Create an instance of Axios\n *\n * @param {Object} defaultConfig The default config for the instance\n * @return {Axios} A new instance of Axios\n */\nfunction createInstance(defaultConfig) {\n var context = new Axios(defaultConfig);\n var instance = bind(Axios.prototype.request, context);\n\n // Copy axios.prototype to instance\n utils.extend(instance, Axios.prototype, context);\n\n // Copy context to instance\n utils.extend(instance, context);\n\n return instance;\n}\n\n// Create the default instance to be exported\nvar axios = createInstance(defaults);\n\n// Expose Axios class to allow class inheritance\naxios.Axios = Axios;\n\n// Factory for creating new instances\naxios.create = function create(instanceConfig) {\n return createInstance(mergeConfig(axios.defaults, instanceConfig));\n};\n\n// Expose Cancel & CancelToken\naxios.Cancel = require('./cancel/Cancel');\naxios.CancelToken = require('./cancel/CancelToken');\naxios.isCancel = require('./cancel/isCancel');\n\n// Expose all/spread\naxios.all = function all(promises) {\n return Promise.all(promises);\n};\naxios.spread = require('./helpers/spread');\n\n// Expose isAxiosError\naxios.isAxiosError = require('./helpers/isAxiosError');\n\nmodule.exports = axios;\n\n// Allow use of default import syntax in TypeScript\nmodule.exports.default = axios;\n","module.exports = require('./lib/axios');","\nimport { setLogger } from '../ulity/debug';\nimport * as debug from '../ulity/debug';\nimport Event from '../ulity/event';\nimport Events from '../base/event';\nimport axios from 'axios';\nimport * as Base from '../base/export';\n\nexport default class RTCEndpoint extends Event\n{\n constructor(options)\n {\n super('RTCPusherPlayer');\n this.TAG = '[RTCPusherPlayer]';\n\n let defaults = {\n element: '',// html video element\n debug: false,// if output debug log\n zlmsdpUrl:'',\n simulcast:false,\n useCamera:true,\n audioEnable:true,\n videoEnable:true,\n recvOnly:false,\n resolution:{w:0,h:0},\n usedatachannel:false,\n };\n \n this.options = Object.assign({}, defaults, options);\n\n if(this.options.debug)\n {\n setLogger();\n }\n\n this.e = {\n onicecandidate:this._onIceCandidate.bind(this),\n ontrack:this._onTrack.bind(this),\n onicecandidateerror:this._onIceCandidateError.bind(this),\n onconnectionstatechange:this._onconnectionstatechange.bind(this),\n ondatachannelopen:this._onDataChannelOpen.bind(this),\n ondatachannelmsg:this._onDataChannelMsg.bind(this),\n ondatachannelerr:this._onDataChannelErr.bind(this),\n ondatachannelclose:this._onDataChannelClose.bind(this),\n };\n\n this._remoteStream = null;\n this._localStream = null;\n\n this.pc = new RTCPeerConnection(null);\n\n this.pc.onicecandidate = this.e.onicecandidate;\n this.pc.onicecandidateerror = this.e.onicecandidateerror;\n this.pc.ontrack = this.e.ontrack;\n this.pc.onconnectionstatechange = this.e.onconnectionstatechange;\n\n this.datachannel = null;\n if(this.options.usedatachannel){\n this.datachannel = this.pc.createDataChannel('chat');\n this.datachannel.onclose = this.e.ondatachannelclose;\n this.datachannel.onerror = this.e.ondatachannelerr;\n this.datachannel.onmessage = this.e.ondatachannelmsg;\n this.datachannel.onopen = this.e.ondatachannelopen;\n }\n\n if(!this.options.recvOnly && (this.options.audioEnable || this.options.videoEnable))\n this.start();\n else\n this.receive();\n \n }\n\n receive()\n {\n let audioTransceiver = null;\n let videoTransceiver = null;\n\n //debug.error(this.TAG,'this not implement');\n const AudioTransceiverInit = {\n direction: 'recvonly',\n sendEncodings:[]\n };\n const VideoTransceiverInit= {\n direction: 'recvonly',\n sendEncodings:[],\n };\n\n audioTransceiver = this.pc.addTransceiver('audio',AudioTransceiverInit);\n videoTransceiver = this.pc.addTransceiver('video',VideoTransceiverInit);\n \n this.pc.createOffer().then((desc)=>{\n debug.log(this.TAG,'offer:',desc.sdp);\n this.pc.setLocalDescription(desc).then(() => {\n axios({\n method: 'post',\n url:this.options.zlmsdpUrl,\n responseType:'json',\n data:desc.sdp,\n headers:{\n 'Content-Type':'text/plain;charset=utf-8'\n }\n }).then(response=>{\n let ret = response.data;//JSON.parse(response.data);\n if(ret.code != 0)\n {// mean failed for offer/anwser exchange \n this.dispatch(Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,ret);\n return;\n }\n let anwser = {};\n anwser.sdp = ret.sdp;\n anwser.type = 'answer';\n debug.log(this.TAG,'answer:',ret.sdp);\n\n this.pc.setRemoteDescription(anwser).then(()=>{\n debug.log(this.TAG,'set remote sucess');\n }).catch(e=>{\n debug.error(this.TAG,e);\n });\n });\n });\n }).catch(e=>{\n debug.error(this.TAG,e);\n });\n }\n\n start()\n {\n let videoConstraints = false;\n let audioConstraints = false;\n\n if(this.options.useCamera)\n {\n if(this.options.videoEnable)\n videoConstraints = new Base.VideoTrackConstraints(Base.VideoSourceInfo.CAMERA);\n if(this.options.audioEnable)\n audioConstraints = new Base.AudioTrackConstraints(Base.AudioSourceInfo.MIC);\n }\n else\n {\n if(this.options.videoEnable)\n {\n videoConstraints = new Base.VideoTrackConstraints(Base.VideoSourceInfo.SCREENCAST);\n if(this.options.audioEnable)\n audioConstraints = new Base.AudioTrackConstraints(Base.AudioSourceInfo.SCREENCAST);\n }\n else\n {\n if(this.options.audioEnable)\n audioConstraints = new Base.AudioTrackConstraints(Base.AudioSourceInfo.MIC);\n else\n {// error shared display media not only audio\n debug.error(this.TAG,'error paramter');\n }\n }\n \n }\n\n if(this.options.resolution.w !=0 && this.options.resolution.h!=0 && typeof videoConstraints == 'object'){\n videoConstraints.resolution = new Base.Resolution(this.options.resolution.w ,this.options.resolution.h);\n }\n\n Base.MediaStreamFactory.createMediaStream(new Base.StreamConstraints(\n audioConstraints, videoConstraints)).then(stream => {\n\n this._localStream = stream;\n\n this.dispatch(Events.WEBRTC_ON_LOCAL_STREAM,stream);\n\n const AudioTransceiverInit = {\n direction: 'sendrecv',\n sendEncodings:[]\n };\n const VideoTransceiverInit= {\n direction: 'sendrecv',\n sendEncodings:[],\n };\n \n if(this.options.simulcast && stream.getVideoTracks().length>0)\n {\n VideoTransceiverInit.sendEncodings = [\n { rid: 'h', active: true, maxBitrate: 1000000 },\n { rid: 'm', active: true, maxBitrate: 500000, scaleResolutionDownBy: 2 },\n { rid: 'l', active: true, maxBitrate: 200000, scaleResolutionDownBy: 4 }\n ];\n }\n let audioTransceiver = null;\n let videoTransceiver = null;\n if (this.options.audioEnable) {\n if (stream.getAudioTracks().length > 0) {\n audioTransceiver = this.pc.addTransceiver(stream.getAudioTracks()[0],\n AudioTransceiverInit);\n }\n else {\n AudioTransceiverInit.direction = 'recvonly';\n audioTransceiver = this.pc.addTransceiver('audio', AudioTransceiverInit);\n }\n }\n \n if (this.options.videoEnable) {\n if (stream.getVideoTracks().length > 0) {\n videoTransceiver = this.pc.addTransceiver(stream.getVideoTracks()[0],\n VideoTransceiverInit);\n }\n else {\n VideoTransceiverInit.direction = 'recvonly';\n videoTransceiver = this.pc.addTransceiver('video',\n VideoTransceiverInit);\n }\n }\n\n /*\n stream.getTracks().forEach((track,idx)=>{\n debug.log(this.TAG,track);\n this.pc.addTrack(track);\n });\n */\n this.pc.createOffer().then((desc)=>{\n debug.log(this.TAG,'offer:',desc.sdp);\n this.pc.setLocalDescription(desc).then(() => {\n axios({\n method: 'post',\n url:this.options.zlmsdpUrl,\n responseType:'json',\n data:desc.sdp,\n headers:{\n 'Content-Type':'text/plain;charset=utf-8'\n }\n }).then(response=>{\n let ret = response.data;//JSON.parse(response.data);\n if(ret.code != 0)\n {// mean failed for offer/anwser exchange \n this.dispatch(Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,ret);\n return;\n }\n let anwser = {};\n anwser.sdp = ret.sdp;\n anwser.type = 'answer';\n debug.log(this.TAG,'answer:',ret.sdp);\n \n this.pc.setRemoteDescription(anwser).then(()=>{\n debug.log(this.TAG,'set remote sucess');\n }).catch(e=>{\n debug.error(this.TAG,e);\n });\n });\n });\n }).catch(e=>{\n debug.error(this.TAG,e);\n });\n\n }).catch(e=>{\n this.dispatch(Events.CAPTURE_STREAM_FAILED);\n //debug.error(this.TAG,e);\n });\n \n //const offerOptions = {};\n /*\n if (typeof this.pc.addTransceiver === 'function') {\n // |direction| seems not working on Safari.\n this.pc.addTransceiver('audio', { direction: 'recvonly' });\n this.pc.addTransceiver('video', { direction: 'recvonly' });\n } else {\n offerOptions.offerToReceiveAudio = true;\n offerOptions.offerToReceiveVideo = true;\n }\n */\n\n\n\n }\n _onIceCandidate(event) {\n if (event.candidate) { \n debug.log('Remote ICE candidate: \\n ' + event.candidate.candidate);\n // Send the candidate to the remote peer\n }\n else {\n // All ICE candidates have been sent\n }\n }\n\n _onTrack(event){\n if(this.options.element && event.streams && event.streams.length>0)\n {\n this.options.element.srcObject = event.streams[0];\n this._remoteStream = event.streams[0];\n\n this.dispatch(Events.WEBRTC_ON_REMOTE_STREAMS,event);\n }\n else\n {\n debug.error('element pararm is failed');\n }\n }\n\n _onIceCandidateError(event){\n this.dispatch(Events.WEBRTC_ICE_CANDIDATE_ERROR,event);\n }\n\n _onconnectionstatechange(event) {\n this.dispatch(Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, this.pc.connectionState);\n }\n\n _onDataChannelOpen(event) {\n debug.log(this.TAG,'ondatachannel open:',event);\n this.dispatch(Events.WEBRTC_ON_DATA_CHANNEL_OPEN,event);\n }\n _onDataChannelMsg(event) {\n debug.log(this.TAG,'ondatachannel msg:',event);\n this.dispatch(Events.WEBRTC_ON_DATA_CHANNEL_MSG,event);\n }\n _onDataChannelErr(event){\n debug.log(this.TAG,'ondatachannel err:',event);\n this.dispatch(Events.WEBRTC_ON_DATA_CHANNEL_ERR,event);\n }\n _onDataChannelClose(event){\n debug.log(this.TAG,'ondatachannel close:',event);\n this.dispatch(Events.WEBRTC_ON_DATA_CHANNEL_CLOSE,event);\n }\n sendMsg(data){\n if(this.datachannel !=null){\n this.datachannel.send(data);\n }else{\n debug.error(this.TAG,'data channel is null');\n }\n }\n closeDataChannel(){\n if(this.datachannel){\n this.datachannel.close();\n this.datachannel = null;\n }\n }\n close()\n { \n this.closeDataChannel();\n if(this.pc)\n {\n this.pc.close();\n this.pc=null;\n }\n\n if(this.options)\n {\n this.options=null;\n }\n\n if(this._localStream)\n {\n this._localStream.getTracks().forEach((track,idx)=>{\n track.stop();\n });\n }\n\n if(this._remoteStream)\n {\n this._remoteStream.getTracks().forEach((track,idx)=>{\n track.stop();\n });\n }\n }\n\n get remoteStream()\n {\n return this._remoteStream;\n }\n \n get localStream()\n {\n return this._localStream;\n }\n}\n","import * as mediaformat from './mediaformat';\nimport * as MediaFactory from './mediastream-factory';\n\n\nconst quickScan=[\n {\n 'label': '4K(UHD)',\n 'width': 3840,\n 'height': 2160\n },\n {\n 'label': '1080p(FHD)',\n 'width': 1920,\n 'height': 1080\n },\n {\n 'label': 'UXGA',\n 'width': 1600,\n 'height': 1200,\n 'ratio': '4:3'\n },\n {\n 'label': '720p(HD)',\n 'width': 1280,\n 'height': 720\n },\n {\n 'label': 'SVGA',\n 'width': 800,\n 'height': 600\n },\n {\n 'label': 'VGA',\n 'width': 640,\n 'height': 480\n },\n {\n 'label': '360p(nHD)',\n 'width': 640,\n 'height': 360\n },\n {\n 'label': 'CIF',\n 'width': 352,\n 'height': 288\n },\n {\n 'label': 'QVGA',\n 'width': 320,\n 'height': 240\n },\n {\n 'label': 'QCIF',\n 'width': 176,\n 'height': 144\n },\n {\n 'label': 'QQVGA',\n 'width': 160,\n 'height': 120\n }\n];\n\n\n\n\nexport default function GetSupportCameraResolutions(){\n return new Promise(function (resolve, reject) {\n let resolutions = [];\n let ok = 0;\n let err = 0;\n for (let i = 0; i < quickScan.length; ++i) {\n let videoConstraints = new MediaFactory.VideoTrackConstraints(mediaformat.VideoSourceInfo.CAMERA);\n videoConstraints.resolution = new mediaformat.Resolution(quickScan[i].width, quickScan[i].height);\n\n MediaFactory.MediaStreamFactory.createMediaStream(new MediaFactory.StreamConstraints(\n false, videoConstraints)).then(stream => {\n resolutions.push(quickScan[i]);\n ok++;\n if(ok+err == quickScan.length)\n {\n resolve(resolutions);\n }\n }).catch(e => {\n err++;\n if(ok+err == quickScan.length)\n {\n resolve(resolutions);\n }\n });\n }\n });\n}\n\nexport function GetAllScanResolution()\n{\n return quickScan;\n}\nexport function isSupportResolution(w,h)\n{\n return new Promise(function (resolve, reject) {\n let videoConstraints = new MediaFactory.VideoTrackConstraints(mediaformat.VideoSourceInfo.CAMERA);\n videoConstraints.resolution = new mediaformat.Resolution(w,h);\n\n MediaFactory.MediaStreamFactory.createMediaStream(new MediaFactory.StreamConstraints(\n false, videoConstraints)).then(stream => {\n resolve();\n }).catch(e => {\n reject(e);\n });\n });\n}","import * as events from './base/event';\nimport * as compile from './ulity/version';\nimport * as media from './base/export';\nimport * as endpoint from './endpoint/endpoint';\nimport * as resolution from './base/resolutionfind';\n\n\n\nconsole.log('build date:',compile.BUILD_DATE);\nconsole.log('version:',compile.VERSION);\n\nexport const Events = events.default;\nexport const Media = media;\nexport const Endpoint = endpoint.default;\nexport const GetSupportCameraResolutions = resolution.default;\nexport const GetAllScanResolution = resolution.GetAllScanResolution;\nexport const isSupportResolution = resolution.isSupportResolution;"],"names":["Events","WEBRTC_NOT_SUPPORT","WEBRTC_ICE_CANDIDATE_ERROR","WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED","WEBRTC_ON_REMOTE_STREAMS","WEBRTC_ON_LOCAL_STREAM","WEBRTC_ON_CONNECTION_STATE_CHANGE","WEBRTC_ON_DATA_CHANNEL_OPEN","WEBRTC_ON_DATA_CHANNEL_CLOSE","WEBRTC_ON_DATA_CHANNEL_ERR","WEBRTC_ON_DATA_CHANNEL_MSG","CAPTURE_STREAM_FAILED","VERSION","BUILD_DATE","isFirefox","window","navigator","userAgent","match","isChrome","isEdge","AudioSourceInfo","MIC","SCREENCAST","FILE","MIXED","VideoSourceInfo","CAMERA","TrackKind","AUDIO","VIDEO","AUDIO_AND_VIDEO","Resolution","constructor","width","height","log","isObject","utils.log","shimGetUserMedia","shimGetDisplayMedia","shimOnTrack","utils.wrapPeerConnectionEvent","utils.filterStats","shimPeerConnection","filterIceServers","utils.deprecated","sdp","SDPUtils","shimRTCPeerConnection","utils.compactObject","utils.detectBrowser","utils.extractVersion","utils.disableLog","utils.disableWarnings","chromeShim.shimPeerConnection","commonShim.shimAddIceCandidateNullOrEmpty","chromeShim.shimGetUserMedia","chromeShim.shimMediaStream","chromeShim.shimOnTrack","chromeShim.shimAddTrackRemoveTrack","chromeShim.shimGetSendersWithDtmf","chromeShim.shimGetStats","chromeShim.shimSenderReceiverGetStats","chromeShim.fixNegotiationNeeded","commonShim.shimRTCIceCandidate","commonShim.shimConnectionState","commonShim.shimMaxMessageSize","commonShim.shimSendThrowTypeError","commonShim.removeExtmapAllowMixed","firefoxShim.shimPeerConnection","firefoxShim.shimGetUserMedia","firefoxShim.shimOnTrack","firefoxShim.shimRemoveStream","firefoxShim.shimSenderGetStats","firefoxShim.shimReceiverGetStats","firefoxShim.shimRTCDataChannel","firefoxShim.shimAddTransceiver","firefoxShim.shimGetParameters","firefoxShim.shimCreateOffer","firefoxShim.shimCreateAnswer","edgeShim.shimPeerConnection","edgeShim.shimGetUserMedia","edgeShim.shimGetDisplayMedia","edgeShim.shimReplaceTrack","safariShim.shimRTCIceServerUrls","safariShim.shimCreateOfferLegacy","safariShim.shimCallbacksAPI","safariShim.shimLocalStreamsAPI","safariShim.shimRemoteStreamsAPI","safariShim.shimTrackEventTransceiver","safariShim.shimGetUserMedia","safariShim.shimAudioContext","AudioTrackConstraints","source","Object","values","MediaFormatModule","some","v","TypeError","deviceId","undefined","VideoTrackConstraints","resolution","frameRate","StreamConstraints","audioConstraints","videoConstraints","audio","video","isVideoConstrainsForScreenCast","constraints","MediaStreamFactory","createMediaStream","Promise","reject","utils","mediaConstraints","create","exact","mediaSource","mediaDevices","getDisplayMedia","getUserMedia","logger","errorLogger","setLogger","console","error","message","optionalParams","Event","type","listener","on","event","fn","push","off","index","indexOf","splice","offAll","dispatch","data","map","each","apply","require$$0","require$$1","defaults","InterceptorManager","Cancel","Axios","axios","require$$2","require$$3","require$$4","RTCEndpoint","options","TAG","element","debug","zlmsdpUrl","simulcast","useCamera","audioEnable","videoEnable","recvOnly","w","h","usedatachannel","assign","e","onicecandidate","_onIceCandidate","bind","ontrack","_onTrack","onicecandidateerror","_onIceCandidateError","onconnectionstatechange","_onconnectionstatechange","ondatachannelopen","_onDataChannelOpen","ondatachannelmsg","_onDataChannelMsg","ondatachannelerr","_onDataChannelErr","ondatachannelclose","_onDataChannelClose","_remoteStream","_localStream","pc","RTCPeerConnection","datachannel","createDataChannel","onclose","onerror","onmessage","onopen","start","receive","AudioTransceiverInit","direction","sendEncodings","VideoTransceiverInit","audioTransceiver","addTransceiver","videoTransceiver","createOffer","then","desc","setLocalDescription","method","url","responseType","headers","response","ret","code","anwser","setRemoteDescription","catch","Base","stream","getVideoTracks","length","rid","active","maxBitrate","scaleResolutionDownBy","getAudioTracks","candidate","streams","srcObject","connectionState","sendMsg","send","closeDataChannel","close","getTracks","forEach","track","idx","stop","remoteStream","localStream","quickScan","GetSupportCameraResolutions","resolve","resolutions","ok","err","i","MediaFactory","mediaformat","GetAllScanResolution","isSupportResolution","compile","events","Media","media","Endpoint","endpoint"],"mappings":"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"} \ No newline at end of file diff --git a/web_src/static/js/jessibuca/decoder.js b/web_src/static/js/jessibuca/decoder.js new file mode 100644 index 00000000..52607c75 --- /dev/null +++ b/web_src/static/js/jessibuca/decoder.js @@ -0,0 +1 @@ +!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(require("path"),require("fs"),require("crypto")):"function"==typeof define&&define.amd?define(["path","fs","crypto"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).path,e.fs,e.crypto$1)}(this,(function(e,r,t){"use strict";function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=n(e),a=n(r),i=n(t);var s=function(e,r){return e(r={exports:{}},r.exports),r.exports}((function(e){var r,t=void 0!==t?t:{},n=(t={print:function(e){console.log("Jessibuca: [worker]:",e)},printErr:function(e){console.warn("Jessibuca: [worker]:",e),postMessage({cmd:"wasmError",message:e})}},{});for(r in t)t.hasOwnProperty(r)&&(n[r]=t[r]);var s,u,c,f,l="./this.program";s="object"==typeof window,u="function"==typeof importScripts,c="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,f=!s&&!c&&!u;var d,p,h,m,v,g="";c?(g=u?o.default.dirname(g)+"/":__dirname+"/",d=function(e,r){return m||(m=a.default),v||(v=o.default),e=v.normalize(e),m.readFileSync(e,r?null:"utf8")},h=function(e){var r=d(e,!0);return r.buffer||(r=new Uint8Array(r)),T(r.buffer),r},process.argv.length>1&&(l=process.argv[1].replace(/\\/g,"/")),process.argv.slice(2),e.exports=t,process.on("uncaughtException",(function(e){if(!(e instanceof et))throw e})),process.on("unhandledRejection",te),t.inspect=function(){return"[Emscripten Module object]"}):f?("undefined"!=typeof read&&(d=function(e){return read(e)}),h=function(e){var r;return"function"==typeof readbuffer?new Uint8Array(readbuffer(e)):(T("object"==typeof(r=read(e,"binary"))),r)},"undefined"!=typeof scriptArgs&&scriptArgs,"undefined"!=typeof print&&("undefined"==typeof console&&(console={}),console.log=print,console.warn=console.error="undefined"!=typeof printErr?printErr:print)):(s||u)&&(u?g=self.location.href:"undefined"!=typeof document&&document.currentScript&&(g=document.currentScript.src),g=0!==g.indexOf("blob:")?g.substr(0,g.lastIndexOf("/")+1):"",d=function(e){var r=new XMLHttpRequest;return r.open("GET",e,!1),r.send(null),r.responseText},u&&(h=function(e){var r=new XMLHttpRequest;return r.open("GET",e,!1),r.responseType="arraybuffer",r.send(null),new Uint8Array(r.response)}),p=function(e,r,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.responseType="arraybuffer",n.onload=function(){200==n.status||0==n.status&&n.response?r(n.response):t()},n.onerror=t,n.send(null)});var y=t.print||console.log.bind(console),w=t.printErr||console.warn.bind(console);for(r in n)n.hasOwnProperty(r)&&(t[r]=n[r]);n=null,t.arguments,t.thisProgram&&(l=t.thisProgram),t.quit;var E,b;function _(e){_.shown||(_.shown={}),_.shown[e]||(_.shown[e]=1,w(e))}t.wasmBinary&&(E=t.wasmBinary),t.noExitRuntime,"object"!=typeof WebAssembly&&te("no native wasm support detected");var k=!1;function T(e,r){e||te("Assertion failed: "+r)}var P="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0;function C(e,r,t){for(var n=r+t,o=r;e[o]&&!(o>=n);)++o;if(o-r>16&&e.subarray&&P)return P.decode(e.subarray(r,o));for(var a="";r>10,56320|1023&c)}}else a+=String.fromCharCode((31&i)<<6|s)}else a+=String.fromCharCode(i)}return a}function A(e,r){return e?C(R,e,r):""}function D(e,r,t,n){if(!(n>0))return 0;for(var o=t,a=t+n-1,i=0;i=55296&&s<=57343)s=65536+((1023&s)<<10)|1023&e.charCodeAt(++i);if(s<=127){if(t>=a)break;r[t++]=s}else if(s<=2047){if(t+1>=a)break;r[t++]=192|s>>6,r[t++]=128|63&s}else if(s<=65535){if(t+2>=a)break;r[t++]=224|s>>12,r[t++]=128|s>>6&63,r[t++]=128|63&s}else{if(t+3>=a)break;r[t++]=240|s>>18,r[t++]=128|s>>12&63,r[t++]=128|s>>6&63,r[t++]=128|63&s}}return r[t]=0,t-o}function S(e,r,t){return D(e,R,r,t)}function F(e){for(var r=0,t=0;t=55296&&n<=57343&&(n=65536+((1023&n)<<10)|1023&e.charCodeAt(++t)),n<=127?++r:r+=n<=2047?2:n<=65535?3:4}return r}var x,$,R,M,O,I,j,U,N,B,W="undefined"!=typeof TextDecoder?new TextDecoder("utf-16le"):void 0;function L(e,r){for(var t=e,n=t>>1,o=n+r/2;!(n>=o)&&O[n];)++n;if((t=n<<1)-e>32&&W)return W.decode(R.subarray(e,t));for(var a="",i=0;!(i>=r/2);++i){var s=M[e+2*i>>1];if(0==s)break;a+=String.fromCharCode(s)}return a}function z(e,r,t){if(void 0===t&&(t=2147483647),t<2)return 0;for(var n=r,o=(t-=2)<2*e.length?t/2:e.length,a=0;a>1]=i,r+=2}return M[r>>1]=0,r-n}function H(e){return 2*e.length}function V(e,r){for(var t=0,n="";!(t>=r/4);){var o=I[e+4*t>>2];if(0==o)break;if(++t,o>=65536){var a=o-65536;n+=String.fromCharCode(55296|a>>10,56320|1023&a)}else n+=String.fromCharCode(o)}return n}function X(e,r,t){if(void 0===t&&(t=2147483647),t<4)return 0;for(var n=r,o=n+t-4,a=0;a=55296&&i<=57343)i=65536+((1023&i)<<10)|1023&e.charCodeAt(++a);if(I[r>>2]=i,(r+=4)+4>o)break}return I[r>>2]=0,r-n}function G(e){for(var r=0,t=0;t=55296&&n<=57343&&++t,r+=4}return r}t.INITIAL_MEMORY;var q=[],J=[],Y=[],K=[];var Q=0,Z=null;function ee(e){Q++,t.monitorRunDependencies&&t.monitorRunDependencies(Q)}function re(e){if(Q--,t.monitorRunDependencies&&t.monitorRunDependencies(Q),0==Q&&Z){var r=Z;Z=null,r()}}function te(e){throw t.onAbort&&t.onAbort(e),w(e+=""),k=!0,e="abort("+e+"). Build with -s ASSERTIONS=1 for more info.",new WebAssembly.RuntimeError(e)}function ne(e,r){return String.prototype.startsWith?e.startsWith(r):0===e.indexOf(r)}t.preloadedImages={},t.preloadedAudios={};function oe(e){return ne(e,"data:application/octet-stream;base64,")}function ae(e){return ne(e,"file://")}var ie,se,ue="decoder.wasm";function ce(e){try{if(e==ue&&E)return new Uint8Array(E);if(h)return h(e);throw"both async and sync fetching of the wasm failed"}catch(e){te(e)}}function fe(e){for(;e.length>0;){var r=e.shift();if("function"!=typeof r){var n=r.func;"number"==typeof n?void 0===r.arg?B.get(n)():B.get(n)(r.arg):n(void 0===r.arg?null:r.arg)}else r(t)}}function le(){var e=new Error;if(!e.stack){try{throw new Error}catch(r){e=r}if(!e.stack)return"(no stack trace available)"}return e.stack.toString()}oe(ue)||(ue=function(e){return t.locateFile?t.locateFile(e,g):g+e}(ue));var de={splitPath:function(e){return/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(e).slice(1)},normalizeArray:function(e,r){for(var t=0,n=e.length-1;n>=0;n--){var o=e[n];"."===o?e.splice(n,1):".."===o?(e.splice(n,1),t++):t&&(e.splice(n,1),t--)}if(r)for(;t;t--)e.unshift("..");return e},normalize:function(e){var r="/"===e.charAt(0),t="/"===e.substr(-1);return(e=de.normalizeArray(e.split("/").filter((function(e){return!!e})),!r).join("/"))||r||(e="."),e&&t&&(e+="/"),(r?"/":"")+e},dirname:function(e){var r=de.splitPath(e),t=r[0],n=r[1];return t||n?(n&&(n=n.substr(0,n.length-1)),t+n):"."},basename:function(e){if("/"===e)return"/";var r=(e=(e=de.normalize(e)).replace(/\/$/,"")).lastIndexOf("/");return-1===r?e:e.substr(r+1)},extname:function(e){return de.splitPath(e)[3]},join:function(){var e=Array.prototype.slice.call(arguments,0);return de.normalize(e.join("/"))},join2:function(e,r){return de.normalize(e+"/"+r)}};var pe={resolve:function(){for(var e="",r=!1,t=arguments.length-1;t>=-1&&!r;t--){var n=t>=0?arguments[t]:ge.cwd();if("string"!=typeof n)throw new TypeError("Arguments to path.resolve must be strings");if(!n)return"";e=n+"/"+e,r="/"===n.charAt(0)}return(r?"/":"")+(e=de.normalizeArray(e.split("/").filter((function(e){return!!e})),!r).join("/"))||"."},relative:function(e,r){function t(e){for(var r=0;r=0&&""===e[t];t--);return r>t?[]:e.slice(r,t-r+1)}e=pe.resolve(e).substr(1),r=pe.resolve(r).substr(1);for(var n=t(e.split("/")),o=t(r.split("/")),a=Math.min(n.length,o.length),i=a,s=0;s0?t.slice(0,n).toString("utf-8"):null}else"undefined"!=typeof window&&"function"==typeof window.prompt?null!==(r=window.prompt("Input: "))&&(r+="\n"):"function"==typeof readline&&null!==(r=readline())&&(r+="\n");if(!r)return null;e.input=Xr(r,!0)}return e.input.shift()},put_char:function(e,r){null===r||10===r?(y(C(e.output,0)),e.output=[]):0!=r&&e.output.push(r)},flush:function(e){e.output&&e.output.length>0&&(y(C(e.output,0)),e.output=[])}},default_tty1_ops:{put_char:function(e,r){null===r||10===r?(w(C(e.output,0)),e.output=[]):0!=r&&e.output.push(r)},flush:function(e){e.output&&e.output.length>0&&(w(C(e.output,0)),e.output=[])}}};function me(e){for(var r=function(e,r){return r||(r=16),Math.ceil(e/r)*r}(e,16384),t=Yr(r);e=r)){r=Math.max(r,t*(t<1048576?2:1.125)>>>0),0!=t&&(r=Math.max(r,256));var n=e.contents;e.contents=new Uint8Array(r),e.usedBytes>0&&e.contents.set(n.subarray(0,e.usedBytes),0)}},resizeFileStorage:function(e,r){if(e.usedBytes!=r)if(0==r)e.contents=null,e.usedBytes=0;else{var t=e.contents;e.contents=new Uint8Array(r),t&&e.contents.set(t.subarray(0,Math.min(r,e.usedBytes))),e.usedBytes=r}},node_ops:{getattr:function(e){var r={};return r.dev=ge.isChrdev(e.mode)?e.id:1,r.ino=e.id,r.mode=e.mode,r.nlink=1,r.uid=0,r.gid=0,r.rdev=e.rdev,ge.isDir(e.mode)?r.size=4096:ge.isFile(e.mode)?r.size=e.usedBytes:ge.isLink(e.mode)?r.size=e.link.length:r.size=0,r.atime=new Date(e.timestamp),r.mtime=new Date(e.timestamp),r.ctime=new Date(e.timestamp),r.blksize=4096,r.blocks=Math.ceil(r.size/r.blksize),r},setattr:function(e,r){void 0!==r.mode&&(e.mode=r.mode),void 0!==r.timestamp&&(e.timestamp=r.timestamp),void 0!==r.size&&ve.resizeFileStorage(e,r.size)},lookup:function(e,r){throw ge.genericErrors[44]},mknod:function(e,r,t,n){return ve.createNode(e,r,t,n)},rename:function(e,r,t){if(ge.isDir(e.mode)){var n;try{n=ge.lookupNode(r,t)}catch(e){}if(n)for(var o in n.contents)throw new ge.ErrnoError(55)}delete e.parent.contents[e.name],e.parent.timestamp=Date.now(),e.name=t,r.contents[t]=e,r.timestamp=e.parent.timestamp,e.parent=r},unlink:function(e,r){delete e.contents[r],e.timestamp=Date.now()},rmdir:function(e,r){var t=ge.lookupNode(e,r);for(var n in t.contents)throw new ge.ErrnoError(55);delete e.contents[r],e.timestamp=Date.now()},readdir:function(e){var r=[".",".."];for(var t in e.contents)e.contents.hasOwnProperty(t)&&r.push(t);return r},symlink:function(e,r,t){var n=ve.createNode(e,r,41471,0);return n.link=t,n},readlink:function(e){if(!ge.isLink(e.mode))throw new ge.ErrnoError(28);return e.link}},stream_ops:{read:function(e,r,t,n,o){var a=e.node.contents;if(o>=e.node.usedBytes)return 0;var i=Math.min(e.node.usedBytes-o,n);if(i>8&&a.subarray)r.set(a.subarray(o,o+i),t);else for(var s=0;s0||n+t8)throw new ge.ErrnoError(32);for(var o=de.normalizeArray(e.split("/").filter((function(e){return!!e})),!1),a=ge.root,i="/",s=0;s40)throw new ge.ErrnoError(32)}}return{path:i,node:a}},getPath:function(e){for(var r;;){if(ge.isRoot(e)){var t=e.mount.mountpoint;return r?"/"!==t[t.length-1]?t+"/"+r:t+r:t}r=r?e.name+"/"+r:e.name,e=e.parent}},hashName:function(e,r){for(var t=0,n=0;n>>0)%ge.nameTable.length},hashAddNode:function(e){var r=ge.hashName(e.parent.id,e.name);e.name_next=ge.nameTable[r],ge.nameTable[r]=e},hashRemoveNode:function(e){var r=ge.hashName(e.parent.id,e.name);if(ge.nameTable[r]===e)ge.nameTable[r]=e.name_next;else for(var t=ge.nameTable[r];t;){if(t.name_next===e){t.name_next=e.name_next;break}t=t.name_next}},lookupNode:function(e,r){var t=ge.mayLookup(e);if(t)throw new ge.ErrnoError(t,e);for(var n=ge.hashName(e.id,r),o=ge.nameTable[n];o;o=o.name_next){var a=o.name;if(o.parent.id===e.id&&a===r)return o}return ge.lookup(e,r)},createNode:function(e,r,t,n){var o=new ge.FSNode(e,r,t,n);return ge.hashAddNode(o),o},destroyNode:function(e){ge.hashRemoveNode(e)},isRoot:function(e){return e===e.parent},isMountpoint:function(e){return!!e.mounted},isFile:function(e){return 32768==(61440&e)},isDir:function(e){return 16384==(61440&e)},isLink:function(e){return 40960==(61440&e)},isChrdev:function(e){return 8192==(61440&e)},isBlkdev:function(e){return 24576==(61440&e)},isFIFO:function(e){return 4096==(61440&e)},isSocket:function(e){return 49152==(49152&e)},flagModes:{r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090},modeStringToFlags:function(e){var r=ge.flagModes[e];if(void 0===r)throw new Error("Unknown file open mode: "+e);return r},flagsToPermissionString:function(e){var r=["r","w","rw"][3&e];return 512&e&&(r+="w"),r},nodePermissions:function(e,r){return ge.ignorePermissions||(-1===r.indexOf("r")||292&e.mode)&&(-1===r.indexOf("w")||146&e.mode)&&(-1===r.indexOf("x")||73&e.mode)?0:2},mayLookup:function(e){var r=ge.nodePermissions(e,"x");return r||(e.node_ops.lookup?0:2)},mayCreate:function(e,r){try{ge.lookupNode(e,r);return 20}catch(e){}return ge.nodePermissions(e,"wx")},mayDelete:function(e,r,t){var n;try{n=ge.lookupNode(e,r)}catch(e){return e.errno}var o=ge.nodePermissions(e,"wx");if(o)return o;if(t){if(!ge.isDir(n.mode))return 54;if(ge.isRoot(n)||ge.getPath(n)===ge.cwd())return 10}else if(ge.isDir(n.mode))return 31;return 0},mayOpen:function(e,r){return e?ge.isLink(e.mode)?32:ge.isDir(e.mode)&&("r"!==ge.flagsToPermissionString(r)||512&r)?31:ge.nodePermissions(e,ge.flagsToPermissionString(r)):44},MAX_OPEN_FDS:4096,nextfd:function(e,r){e=e||0,r=r||ge.MAX_OPEN_FDS;for(var t=e;t<=r;t++)if(!ge.streams[t])return t;throw new ge.ErrnoError(33)},getStream:function(e){return ge.streams[e]},createStream:function(e,r,t){ge.FSStream||(ge.FSStream=function(){},ge.FSStream.prototype={object:{get:function(){return this.node},set:function(e){this.node=e}},isRead:{get:function(){return 1!=(2097155&this.flags)}},isWrite:{get:function(){return 0!=(2097155&this.flags)}},isAppend:{get:function(){return 1024&this.flags}}});var n=new ge.FSStream;for(var o in e)n[o]=e[o];e=n;var a=ge.nextfd(r,t);return e.fd=a,ge.streams[a]=e,e},closeStream:function(e){ge.streams[e]=null},chrdev_stream_ops:{open:function(e){var r=ge.getDevice(e.node.rdev);e.stream_ops=r.stream_ops,e.stream_ops.open&&e.stream_ops.open(e)},llseek:function(){throw new ge.ErrnoError(70)}},major:function(e){return e>>8},minor:function(e){return 255&e},makedev:function(e,r){return e<<8|r},registerDevice:function(e,r){ge.devices[e]={stream_ops:r}},getDevice:function(e){return ge.devices[e]},getMounts:function(e){for(var r=[],t=[e];t.length;){var n=t.pop();r.push(n),t.push.apply(t,n.mounts)}return r},syncfs:function(e,r){"function"==typeof e&&(r=e,e=!1),ge.syncFSRequests++,ge.syncFSRequests>1&&w("warning: "+ge.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work");var t=ge.getMounts(ge.root.mount),n=0;function o(e){return ge.syncFSRequests--,r(e)}function a(e){if(e)return a.errored?void 0:(a.errored=!0,o(e));++n>=t.length&&o(null)}t.forEach((function(r){if(!r.type.syncfs)return a(null);r.type.syncfs(r,e,a)}))},mount:function(e,r,t){var n,o="/"===t,a=!t;if(o&&ge.root)throw new ge.ErrnoError(10);if(!o&&!a){var i=ge.lookupPath(t,{follow_mount:!1});if(t=i.path,n=i.node,ge.isMountpoint(n))throw new ge.ErrnoError(10);if(!ge.isDir(n.mode))throw new ge.ErrnoError(54)}var s={type:e,opts:r,mountpoint:t,mounts:[]},u=e.mount(s);return u.mount=s,s.root=u,o?ge.root=u:n&&(n.mounted=s,n.mount&&n.mount.mounts.push(s)),u},unmount:function(e){var r=ge.lookupPath(e,{follow_mount:!1});if(!ge.isMountpoint(r.node))throw new ge.ErrnoError(28);var t=r.node,n=t.mounted,o=ge.getMounts(n);Object.keys(ge.nameTable).forEach((function(e){for(var r=ge.nameTable[e];r;){var t=r.name_next;-1!==o.indexOf(r.mount)&&ge.destroyNode(r),r=t}})),t.mounted=null;var a=t.mount.mounts.indexOf(n);t.mount.mounts.splice(a,1)},lookup:function(e,r){return e.node_ops.lookup(e,r)},mknod:function(e,r,t){var n=ge.lookupPath(e,{parent:!0}).node,o=de.basename(e);if(!o||"."===o||".."===o)throw new ge.ErrnoError(28);var a=ge.mayCreate(n,o);if(a)throw new ge.ErrnoError(a);if(!n.node_ops.mknod)throw new ge.ErrnoError(63);return n.node_ops.mknod(n,o,r,t)},create:function(e,r){return r=void 0!==r?r:438,r&=4095,r|=32768,ge.mknod(e,r,0)},mkdir:function(e,r){return r=void 0!==r?r:511,r&=1023,r|=16384,ge.mknod(e,r,0)},mkdirTree:function(e,r){for(var t=e.split("/"),n="",o=0;othis.length-1||e<0)){var r=e%this.chunkSize,t=e/this.chunkSize|0;return this.getter(t)[r]}},a.prototype.setDataGetter=function(e){this.getter=e},a.prototype.cacheLength=function(){var e=new XMLHttpRequest;if(e.open("HEAD",t,!1),e.send(null),!(e.status>=200&&e.status<300||304===e.status))throw new Error("Couldn't load "+t+". Status: "+e.status);var r,n=Number(e.getResponseHeader("Content-length")),o=(r=e.getResponseHeader("Accept-Ranges"))&&"bytes"===r,a=(r=e.getResponseHeader("Content-Encoding"))&&"gzip"===r,i=1048576;o||(i=n);var s=this;s.setDataGetter((function(e){var r=e*i,o=(e+1)*i-1;if(o=Math.min(o,n-1),void 0===s.chunks[e]&&(s.chunks[e]=function(e,r){if(e>r)throw new Error("invalid range ("+e+", "+r+") or no bytes requested!");if(r>n-1)throw new Error("only "+n+" bytes available! programmer error!");var o=new XMLHttpRequest;if(o.open("GET",t,!1),n!==i&&o.setRequestHeader("Range","bytes="+e+"-"+r),"undefined"!=typeof Uint8Array&&(o.responseType="arraybuffer"),o.overrideMimeType&&o.overrideMimeType("text/plain; charset=x-user-defined"),o.send(null),!(o.status>=200&&o.status<300||304===o.status))throw new Error("Couldn't load "+t+". Status: "+o.status);return void 0!==o.response?new Uint8Array(o.response||[]):Xr(o.responseText||"",!0)}(r,o)),void 0===s.chunks[e])throw new Error("doXHR failed!");return s.chunks[e]})),!a&&n||(i=n=1,n=this.getter(0).length,i=n,y("LazyFiles on gzip forces download of the whole file when length is accessed")),this._length=n,this._chunkSize=i,this.lengthKnown=!0},"undefined"!=typeof XMLHttpRequest){if(!u)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var i=new a;Object.defineProperties(i,{length:{get:function(){return this.lengthKnown||this.cacheLength(),this._length}},chunkSize:{get:function(){return this.lengthKnown||this.cacheLength(),this._chunkSize}}});var s={isDevice:!1,contents:i}}else s={isDevice:!1,url:t};var c=ge.createFile(e,r,s,n,o);s.contents?c.contents=s.contents:s.url&&(c.contents=null,c.url=s.url),Object.defineProperties(c,{usedBytes:{get:function(){return this.contents.length}}});var f={};return Object.keys(c.stream_ops).forEach((function(e){var r=c.stream_ops[e];f[e]=function(){return ge.forceLoadFile(c),r.apply(null,arguments)}})),f.read=function(e,r,t,n,o){ge.forceLoadFile(c);var a=e.node.contents;if(o>=a.length)return 0;var i=Math.min(a.length-o,n);if(a.slice)for(var s=0;s>2]=n.dev,I[t+4>>2]=0,I[t+8>>2]=n.ino,I[t+12>>2]=n.mode,I[t+16>>2]=n.nlink,I[t+20>>2]=n.uid,I[t+24>>2]=n.gid,I[t+28>>2]=n.rdev,I[t+32>>2]=0,se=[n.size>>>0,(ie=n.size,+Math.abs(ie)>=1?ie>0?(0|Math.min(+Math.floor(ie/4294967296),4294967295))>>>0:~~+Math.ceil((ie-+(~~ie>>>0))/4294967296)>>>0:0)],I[t+40>>2]=se[0],I[t+44>>2]=se[1],I[t+48>>2]=4096,I[t+52>>2]=n.blocks,I[t+56>>2]=n.atime.getTime()/1e3|0,I[t+60>>2]=0,I[t+64>>2]=n.mtime.getTime()/1e3|0,I[t+68>>2]=0,I[t+72>>2]=n.ctime.getTime()/1e3|0,I[t+76>>2]=0,se=[n.ino>>>0,(ie=n.ino,+Math.abs(ie)>=1?ie>0?(0|Math.min(+Math.floor(ie/4294967296),4294967295))>>>0:~~+Math.ceil((ie-+(~~ie>>>0))/4294967296)>>>0:0)],I[t+80>>2]=se[0],I[t+84>>2]=se[1],0},doMsync:function(e,r,t,n,o){var a=R.slice(e,e+t);ge.msync(r,a,o,t,n)},doMkdir:function(e,r){return"/"===(e=de.normalize(e))[e.length-1]&&(e=e.substr(0,e.length-1)),ge.mkdir(e,r,0),0},doMknod:function(e,r,t){switch(61440&r){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}return ge.mknod(e,r,t),0},doReadlink:function(e,r,t){if(t<=0)return-28;var n=ge.readlink(e),o=Math.min(t,F(n)),a=$[r+o];return S(n,r,t+1),$[r+o]=a,o},doAccess:function(e,r){if(-8&r)return-28;var t;if(!(t=ge.lookupPath(e,{follow:!0}).node))return-44;var n="";return 4&r&&(n+="r"),2&r&&(n+="w"),1&r&&(n+="x"),n&&ge.nodePermissions(t,n)?-2:0},doDup:function(e,r,t){var n=ge.getStream(t);return n&&ge.close(n),ge.open(e,r,0,t,t).fd},doReadv:function(e,r,t,n){for(var o=0,a=0;a>2],s=I[r+(8*a+4)>>2],u=ge.read(e,$,i,s,n);if(u<0)return-1;if(o+=u,u>2],s=I[r+(8*a+4)>>2],u=ge.write(e,$,i,s,n);if(u<0)return-1;o+=u}return o},varargs:void 0,get:function(){return ye.varargs+=4,I[ye.varargs-4>>2]},getStr:function(e){return A(e)},getStreamFromFD:function(e){var r=ge.getStream(e);if(!r)throw new ge.ErrnoError(8);return r},get64:function(e,r){return e}};function we(e){switch(e){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+e)}}var Ee=void 0;function be(e){for(var r="",t=e;R[t];)r+=Ee[R[t++]];return r}var _e={},ke={},Te={};function Pe(e){if(void 0===e)return"_unknown";var r=(e=e.replace(/[^a-zA-Z0-9_]/g,"$")).charCodeAt(0);return r>=48&&r<=57?"_"+e:e}function Ce(e,r){return e=Pe(e),new Function("body","return function "+e+'() {\n "use strict"; return body.apply(this, arguments);\n};\n')(r)}function Ae(e,r){var t=Ce(r,(function(e){this.name=r,this.message=e;var t=new Error(e).stack;void 0!==t&&(this.stack=this.toString()+"\n"+t.replace(/^Error(:[^\n]*)?\n/,""))}));return t.prototype=Object.create(e.prototype),t.prototype.constructor=t,t.prototype.toString=function(){return void 0===this.message?this.name:this.name+": "+this.message},t}var De=void 0;function Se(e){throw new De(e)}var Fe=void 0;function xe(e){throw new Fe(e)}function $e(e,r,t){function n(r){var n=t(r);n.length!==e.length&&xe("Mismatched type converter count");for(var o=0;o>2])}function tr(e){return this.rawGetPointee&&(e=this.rawGetPointee(e)),e}function nr(e){this.rawDestructor&&this.rawDestructor(e)}function or(e){null!==e&&e.delete()}function ar(e,r,t){if(r===t)return e;if(void 0===t.baseClass)return null;var n=ar(e,r,t.baseClass);return null===n?null:t.downcast(n)}function ir(){return Object.keys(cr).length}function sr(){var e=[];for(var r in cr)cr.hasOwnProperty(r)&&e.push(cr[r]);return e}function ur(e){ze=e,He.length&&ze&&ze(Ve)}var cr={};function fr(e,r){return r=function(e,r){for(void 0===r&&Se("ptr should not be undefined");e.baseClass;)r=e.upcast(r),e=e.baseClass;return r}(e,r),cr[r]}function lr(e,r){return r.ptrType&&r.ptr||xe("makeClassHandle requires ptr and ptrType"),!!r.smartPtrType!==!!r.smartPtr&&xe("Both smartPtrType and smartPtr must be specified"),r.count={value:1},Ne(Object.create(e,{$$:{value:r}}))}function dr(e){var r=this.getPointee(e);if(!r)return this.destructor(e),null;var t=fr(this.registeredClass,r);if(void 0!==t){if(0===t.$$.count.value)return t.$$.ptr=r,t.$$.smartPtr=e,t.clone();var n=t.clone();return this.destructor(e),n}function o(){return this.isSmartPointer?lr(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:r,smartPtrType:this,smartPtr:e}):lr(this.registeredClass.instancePrototype,{ptrType:this,ptr:e})}var a,i=this.registeredClass.getActualType(r),s=qe[i];if(!s)return o.call(this);a=this.isConst?s.constPointerType:s.pointerType;var u=ar(r,this.registeredClass,a.registeredClass);return null===u?o.call(this):this.isSmartPointer?lr(a.registeredClass.instancePrototype,{ptrType:a,ptr:u,smartPtrType:this,smartPtr:e}):lr(a.registeredClass.instancePrototype,{ptrType:a,ptr:u})}function pr(e,r,t,n,o,a,i,s,u,c,f){this.name=e,this.registeredClass=r,this.isReference=t,this.isConst=n,this.isSmartPointer=o,this.pointeeType=a,this.sharingPolicy=i,this.rawGetPointee=s,this.rawConstructor=u,this.rawShare=c,this.rawDestructor=f,o||void 0!==r.baseClass?this.toWireType=Ze:n?(this.toWireType=Qe,this.destructorFunction=null):(this.toWireType=er,this.destructorFunction=null)}function hr(e,r,n){return-1!=e.indexOf("j")?function(e,r,n){var o=t["dynCall_"+e];return n&&n.length?o.apply(null,[r].concat(n)):o.call(null,r)}(e,r,n):B.get(r).apply(null,n)}function mr(e,r){var t,n,o,a=-1!=(e=be(e)).indexOf("j")?(t=e,n=r,o=[],function(){o.length=arguments.length;for(var e=0;e>2)+n]);return t}function Er(e){for(;e.length;){var r=e.pop();e.pop()(r)}}function br(e,r){if(!(e instanceof Function))throw new TypeError("new_ called with constructor type "+typeof e+" which is not a function");var t=Ce(e.name||"unknownFunctionName",(function(){}));t.prototype=e.prototype;var n=new t,o=e.apply(n,r);return o instanceof Object?o:n}function _r(e,r,t){return e instanceof Object||Se(t+' with invalid "this": '+e),e instanceof r.registeredClass.constructor||Se(t+' incompatible with "this" of type '+e.constructor.name),e.$$.ptr||Se("cannot call emscripten binding method "+t+" on deleted object"),Ke(e.$$.ptr,e.$$.ptrType.registeredClass,r.registeredClass)}var kr=[],Tr=[{},{value:void 0},{value:null},{value:!0},{value:!1}];function Pr(e){e>4&&0==--Tr[e].refcount&&(Tr[e]=void 0,kr.push(e))}function Cr(){for(var e=0,r=5;r>2])};case 3:return function(e){return this.fromWireType(N[e>>3])};default:throw new TypeError("Unknown float type: "+e)}}function xr(e,r,t){switch(r){case 0:return t?function(e){return $[e]}:function(e){return R[e]};case 1:return t?function(e){return M[e>>1]}:function(e){return O[e>>1]};case 2:return t?function(e){return I[e>>2]}:function(e){return j[e>>2]};default:throw new TypeError("Unknown integer type: "+e)}}function $r(e){return e||Se("Cannot use deleted val. handle = "+e),Tr[e].value}function Rr(e,r){var t=ke[e];return void 0===t&&Se(r+" has unknown type "+gr(e)),t}var Mr={};var Or=[];function Ir(e,r){return(e>>>0)+4294967296*r}function jr(e,r){if(e<=0)return e;var t=r<=32?Math.abs(1<=t&&(r<=32||e>t)&&(e=-2*t+e),e}function Ur(e,r){return e>=0?e:r<=32?2*Math.abs(1<0?"\n":"")+function(e){var r=le(),t=r.lastIndexOf("_emscripten_log"),n=r.lastIndexOf("_emscripten_get_callstack"),o=r.indexOf("\n",Math.max(t,n))+1;r=r.slice(o),32&e&&_("EM_LOG_DEMANGLE is deprecated; ignoring"),8&e&&"undefined"==typeof emscripten_source_map&&(_('Source map information is not available, emscripten_log with EM_LOG_C_STACK will be ignored. Build with "--pre-js $EMSCRIPTEN/src/emscripten-source-map.min.js" linker flag to add source map loading to code.'),e^=8,e|=16);var a=null;if(128&e)for(a=Nr(arguments);a[1].indexOf("_emscripten_")>=0;)a=Nr(a[0]);var i=r.split("\n");r="";var s=new RegExp("\\s*(.*?)@(.*?):([0-9]+):([0-9]+)"),u=new RegExp("\\s*(.*?)@(.*):(.*)(:(.*))?"),c=new RegExp("\\s*at (.*?) \\((.*):(.*):(.*)\\)");for(var f in i){var l=i[f],d="",p="",h=0,m=0,v=c.exec(l);if(v&&5==v.length)d=v[1],p=v[2],h=v[3],m=v[4];else{if((v=s.exec(l))||(v=u.exec(l)),!(v&&v.length>=4)){r+=l+"\n";continue}d=v[1],p=v[2],h=v[3],m=0|v[4]}var g=!1;if(8&e){var y=emscripten_source_map.originalPositionFor({line:h,column:m});(g=y&&y.source)&&(64&e&&(y.source=y.source.substring(y.source.replace(/\\/g,"/").lastIndexOf("/")+1)),r+=" at "+d+" ("+y.source+":"+y.line+":"+y.column+")\n")}(16&e||!g)&&(64&e&&(p=p.substring(p.replace(/\\/g,"/").lastIndexOf("/")+1)),r+=(g?" = "+d:" at "+d)+" ("+p+":"+h+":"+m+")\n"),128&e&&a[0]&&(a[1]==d&&a[2].length>0&&(r=r.replace(/\s+$/,""),r+=" with values: "+a[1]+a[2]+"\n"),a=Nr(a[0]))}return r.replace(/\s+$/,"")}(e)),1&e?4&e?console.error(r):2&e?console.warn(r):512&e?console.info(r):256&e?console.debug(r):console.log(r):6&e?w(r):y(r)}var Wr={};function Lr(){if(!Lr.strings){var e={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:l||"./this.program"};for(var r in Wr)e[r]=Wr[r];var t=[];for(var r in e)t.push(r+"="+e[r]);Lr.strings=t}return Lr.strings}var zr=function(e,r,t,n){e||(e=this),this.parent=e,this.mount=e.mount,this.mounted=null,this.id=ge.nextInode++,this.name=r,this.mode=t,this.node_ops={},this.stream_ops={},this.rdev=n},Hr=365,Vr=146;function Xr(e,r,t){var n=t>0?t:F(e)+1,o=new Array(n),a=D(e,o,0,o.length);return r&&(o.length=a),o}Object.defineProperties(zr.prototype,{read:{get:function(){return(this.mode&Hr)===Hr},set:function(e){e?this.mode|=Hr:this.mode&=-366}},write:{get:function(){return(this.mode&Vr)===Vr},set:function(e){e?this.mode|=Vr:this.mode&=-147}},isFolder:{get:function(){return ge.isDir(this.mode)}},isDevice:{get:function(){return ge.isChrdev(this.mode)}}}),ge.FSNode=zr,ge.staticInit(),function(){for(var e=new Array(256),r=0;r<256;++r)e[r]=String.fromCharCode(r);Ee=e}(),De=t.BindingError=Ae(Error,"BindingError"),Fe=t.InternalError=Ae(Error,"InternalError"),Ge.prototype.isAliasOf=Me,Ge.prototype.clone=Be,Ge.prototype.delete=We,Ge.prototype.isDeleted=Le,Ge.prototype.deleteLater=Xe,pr.prototype.getPointee=tr,pr.prototype.destructor=nr,pr.prototype.argPackAdvance=8,pr.prototype.readValueFromPointer=rr,pr.prototype.deleteObject=or,pr.prototype.fromWireType=dr,t.getInheritedInstanceCount=ir,t.getLiveInheritedInstances=sr,t.flushPendingDeletes=Ve,t.setDelayFunction=ur,vr=t.UnboundTypeError=Ae(Error,"UnboundTypeError"),t.count_emval_handles=Cr,t.get_first_emval=Ar;var Gr={x:function(e,r,t){ye.varargs=t;try{var n=ye.getStreamFromFD(e);switch(r){case 0:return(o=ye.get())<0?-28:ge.open(n.path,n.flags,0,o).fd;case 1:case 2:case 13:case 14:return 0;case 3:return n.flags;case 4:var o=ye.get();return n.flags|=o,0;case 12:o=ye.get();return M[o+0>>1]=2,0;case 16:case 8:default:return-28;case 9:return a=28,I[Kr()>>2]=a,-1}}catch(e){return void 0!==ge&&e instanceof ge.ErrnoError||te(e),-e.errno}var a},w:function(e,r,t){ye.varargs=t;try{var n=ye.getStr(e),o=t?ye.get():0;return ge.open(n,r,o).fd}catch(e){return void 0!==ge&&e instanceof ge.ErrnoError||te(e),-e.errno}},C:function(e,r,t,n,o){var a=we(t);Re(e,{name:r=be(r),fromWireType:function(e){return!!e},toWireType:function(e,r){return r?n:o},argPackAdvance:8,readValueFromPointer:function(e){var n;if(1===t)n=$;else if(2===t)n=M;else{if(4!==t)throw new TypeError("Unknown boolean type size: "+r);n=I}return this.fromWireType(n[e>>a])},destructorFunction:null})},n:function(e,r,n,o,a,i,s,u,c,f,l,d,p){l=be(l),i=mr(a,i),u&&(u=mr(s,u)),f&&(f=mr(c,f)),p=mr(d,p);var h=Pe(l);!function(e,r,n){t.hasOwnProperty(e)?((void 0===n||void 0!==t[e].overloadTable&&void 0!==t[e].overloadTable[n])&&Se("Cannot register public name '"+e+"' twice"),Je(t,e,e),t.hasOwnProperty(n)&&Se("Cannot register multiple overloads of a function with the same number of arguments ("+n+")!"),t[e].overloadTable[n]=r):(t[e]=r,void 0!==n&&(t[e].numArguments=n))}(h,(function(){yr("Cannot construct "+l+" due to unbound types",[o])})),$e([e,r,n],o?[o]:[],(function(r){var n,a;r=r[0],a=o?(n=r.registeredClass).instancePrototype:Ge.prototype;var s=Ce(h,(function(){if(Object.getPrototypeOf(this)!==c)throw new De("Use 'new' to construct "+l);if(void 0===d.constructor_body)throw new De(l+" has no accessible constructor");var e=d.constructor_body[arguments.length];if(void 0===e)throw new De("Tried to invoke ctor of "+l+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(d.constructor_body).toString()+") parameters instead!");return e.apply(this,arguments)})),c=Object.create(a,{constructor:{value:s}});s.prototype=c;var d=new Ye(l,s,c,p,n,i,u,f),m=new pr(l,d,!0,!1,!1),v=new pr(l+"*",d,!1,!1,!1),g=new pr(l+" const*",d,!1,!0,!1);return qe[e]={pointerType:v,constPointerType:g},function(e,r,n){t.hasOwnProperty(e)||xe("Replacing nonexistant public symbol"),void 0!==t[e].overloadTable&&void 0!==n?t[e].overloadTable[n]=r:(t[e]=r,t[e].argCount=n)}(h,s),[m,v,g]}))},i:function(e,r,t,n,o,a){T(r>0);var i=wr(r,t);o=mr(n,o);var s=[a],u=[];$e([],[e],(function(e){var t="constructor "+(e=e[0]).name;if(void 0===e.registeredClass.constructor_body&&(e.registeredClass.constructor_body=[]),void 0!==e.registeredClass.constructor_body[r-1])throw new De("Cannot register multiple constructors with identical number of parameters ("+(r-1)+") for class '"+e.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!");return e.registeredClass.constructor_body[r-1]=function(){yr("Cannot construct "+e.name+" due to unbound types",i)},$e([],i,(function(n){return e.registeredClass.constructor_body[r-1]=function(){arguments.length!==r-1&&Se(t+" called with "+arguments.length+" arguments, expected "+(r-1)),u.length=0,s.length=r;for(var e=1;e0?", ":"")+l),d+=(c?"var rv = ":"")+"invoker(fn"+(l.length>0?", ":"")+l+");\n",s)d+="runDestructors(destructors);\n";else for(u=i?1:2;u>>s}}var u=-1!=r.indexOf("unsigned");Re(e,{name:r,fromWireType:i,toWireType:function(e,t){if("number"!=typeof t&&"boolean"!=typeof t)throw new TypeError('Cannot convert "'+Sr(t)+'" to '+this.name);if(to)throw new TypeError('Passing a number "'+Sr(t)+'" from JS side to C/C++ side to an argument of type "'+r+'", which is outside the valid range ['+n+", "+o+"]!");return u?t>>>0:0|t},argPackAdvance:8,readValueFromPointer:xr(r,a,0!==n),destructorFunction:null})},b:function(e,r,t){var n=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][r];function o(e){var r=j,t=r[e>>=2],o=r[e+1];return new n(x,o,t)}Re(e,{name:t=be(t),fromWireType:o,argPackAdvance:8,readValueFromPointer:o},{ignoreDuplicateRegistrations:!0})},m:function(e,r){var t="std::string"===(r=be(r));Re(e,{name:r,fromWireType:function(e){var r,n=j[e>>2];if(t)for(var o=e+4,a=0;a<=n;++a){var i=e+4+a;if(a==n||0==R[i]){var s=A(o,i-o);void 0===r?r=s:(r+=String.fromCharCode(0),r+=s),o=i+1}}else{var u=new Array(n);for(a=0;a>2]=o,t&&n)S(r,a+4,o+1);else if(n)for(var i=0;i255&&(Jr(a),Se("String has UTF-16 code units that do not fit in 8 bits")),R[a+4+i]=s}else for(i=0;i>2],i=a(),u=e+4,c=0;c<=o;++c){var f=e+4+c*r;if(c==o||0==i[f>>s]){var l=n(u,f-u);void 0===t?t=l:(t+=String.fromCharCode(0),t+=l),u=f+r}}return Jr(e),t},toWireType:function(e,n){"string"!=typeof n&&Se("Cannot pass non-string to C++ string type "+t);var a=i(n),u=Yr(4+a+r);return j[u>>2]=a>>s,o(n,u+4,a+r),null!==e&&e.push(Jr,u),u},argPackAdvance:8,readValueFromPointer:rr,destructorFunction:function(e){Jr(e)}})},D:function(e,r){Re(e,{isVoid:!0,name:r=be(r),argPackAdvance:0,fromWireType:function(){},toWireType:function(e,r){}})},u:function(e,r,t){e=$r(e),r=Rr(r,"emval::as");var n=[],o=Dr(n);return I[t>>2]=o,r.toWireType(n,e)},e:function(e,r,t,n){var o,a;(e=Or[e])(r=$r(r),t=void 0===(a=Mr[o=t])?be(o):a,null,n)},p:Pr,d:function(e,r){for(var t=function(e,r){for(var t=new Array(e),n=0;n>2)+n],"parameter "+n);return t}(e,r),n=t[0],o=n.name+"_$"+t.slice(1).map((function(e){return e.name})).join("_")+"$",a=["retType"],i=[n],s="",u=0;u4&&(Tr[e].refcount+=1)},q:function(e){Er(Tr[e].value),Pr(e)},A:function(e,r){return Dr((e=Rr(e,"_emval_take_value")).readValueFromPointer(r))},a:function(){te()},F:function e(){return void 0===e.start&&(e.start=Date.now()),1e3*(Date.now()-e.start)|0},G:function(e,r,t){var n=function(e,r){var t=e,n=r;function o(e){var r;return n=function(e,r){return"double"!==r&&"i64"!==r||7&e&&(e+=4),e}(n,e),"double"===e?(r=N[n>>3],n+=8):"i64"==e?(r=[I[n>>2],I[n+4>>2]],n+=8):(e="i32",r=I[n>>2],n+=4),r}for(var a,i,s,u,c=[];;){var f=t;if(0===(a=$[t>>0]))break;if(i=$[t+1>>0],37==a){var l=!1,d=!1,p=!1,h=!1,m=!1;e:for(;;){switch(i){case 43:l=!0;break;case 45:d=!0;break;case 35:p=!0;break;case 48:if(h)break e;h=!0;break;case 32:m=!0;break;default:break e}t++,i=$[t+1>>0]}var v=0;if(42==i)v=o("i32"),t++,i=$[t+1>>0];else for(;i>=48&&i<=57;)v=10*v+(i-48),t++,i=$[t+1>>0];var g,y=!1,w=-1;if(46==i){if(w=0,y=!0,t++,42==(i=$[t+1>>0]))w=o("i32"),t++;else for(;;){var E=$[t+1>>0];if(E<48||E>57)break;w=10*w+(E-48),t++}i=$[t+1>>0]}switch(w<0&&(w=6,y=!1),String.fromCharCode(i)){case"h":104==$[t+2>>0]?(t++,g=1):g=2;break;case"l":108==$[t+2>>0]?(t++,g=8):g=4;break;case"L":case"q":case"j":g=8;break;case"z":case"t":case"I":g=4;break;default:g=null}switch(g&&t++,i=$[t+1>>0],String.fromCharCode(i)){case"d":case"i":case"u":case"o":case"x":case"X":case"p":var b=100==i||105==i;s=o("i"+8*(g=g||4)),8==g&&(s=117==i?(s[0]>>>0)+4294967296*(s[1]>>>0):Ir(s[0],s[1])),g<=4&&(s=(b?jr:Ur)(s&Math.pow(256,g)-1,8*g));var _=Math.abs(s),k="";if(100==i||105==i)C=jr(s,8*g).toString(10);else if(117==i)C=Ur(s,8*g).toString(10),s=Math.abs(s);else if(111==i)C=(p?"0":"")+_.toString(8);else if(120==i||88==i){if(k=p&&0!=s?"0x":"",s<0){s=-s,C=(_-1).toString(16);for(var T=[],P=0;P=0&&(l?k="+"+k:m&&(k=" "+k)),"-"==C.charAt(0)&&(k="-"+k,C=C.substr(1));k.length+C.lengthS&&S>=-4?(i=(103==i?"f":"F").charCodeAt(0),w-=S+1):(i=(103==i?"e":"E").charCodeAt(0),w--),D=Math.min(w,20)}101==i||69==i?(C=s.toExponential(D),/[eE][-+]\d$/.test(C)&&(C=C.slice(0,-1)+"0"+C.slice(-1))):102!=i&&70!=i||(C=s.toFixed(D),0===s&&((u=s)<0||0===u&&1/u==-1/0)&&(C="-"+C));var F=C.split("e");if(A&&!p)for(;F[0].length>1&&-1!=F[0].indexOf(".")&&("0"==F[0].slice(-1)||"."==F[0].slice(-1));)F[0]=F[0].slice(0,-1);else for(p&&-1==C.indexOf(".")&&(F[0]+=".");w>D++;)F[0]+="0";C=F[0]+(F.length>1?"e"+F[1]:""),69==i&&(C=C.toUpperCase()),s>=0&&(l?C="+"+C:m&&(C=" "+C))}else C=(s<0?"-":"")+"inf",h=!1;for(;C.length>0]);else c=c.concat(Xr("(null)".substr(0,M),!0));if(d)for(;M0;)c.push(32);d||c.push(o("i8"));break;case"n":var O=o("i32*");I[O>>2]=c.length;break;case"%":c.push(a);break;default:for(P=f;P>0])}t+=2}else c.push(a),t+=1}return c}(r,t);Br(e,C(n,0))},s:function(e){R.length,te("OOM")},t:function(e,r){try{var t=0;return Lr().forEach((function(n,o){var a=r+t;I[e+4*o>>2]=a,function(e,r,t){for(var n=0;n>0]=e.charCodeAt(n);t||($[r>>0]=0)}(n,a),t+=n.length+1})),0}catch(e){return void 0!==ge&&e instanceof ge.ErrnoError||te(e),e.errno}},v:function(e,r){try{var t=Lr();I[e>>2]=t.length;var n=0;return t.forEach((function(e){n+=e.length+1})),I[r>>2]=n,0}catch(e){return void 0!==ge&&e instanceof ge.ErrnoError||te(e),e.errno}},k:function(e){try{var r=ye.getStreamFromFD(e);return ge.close(r),0}catch(e){return void 0!==ge&&e instanceof ge.ErrnoError||te(e),e.errno}},y:function(e,r){try{var t=ye.getStreamFromFD(e),n=t.tty?2:ge.isDir(t.mode)?3:ge.isLink(t.mode)?7:4;return $[r>>0]=n,0}catch(e){return void 0!==ge&&e instanceof ge.ErrnoError||te(e),e.errno}},z:function(e,r,t,n){try{var o=ye.getStreamFromFD(e),a=ye.doReadv(o,r,t);return I[n>>2]=a,0}catch(e){return void 0!==ge&&e instanceof ge.ErrnoError||te(e),e.errno}},r:function(e,r,t,n,o){try{var a=ye.getStreamFromFD(e),i=4294967296*t+(r>>>0),s=9007199254740992;return i<=-s||i>=s?-61:(ge.llseek(a,i,n),se=[a.position>>>0,(ie=a.position,+Math.abs(ie)>=1?ie>0?(0|Math.min(+Math.floor(ie/4294967296),4294967295))>>>0:~~+Math.ceil((ie-+(~~ie>>>0))/4294967296)>>>0:0)],I[o>>2]=se[0],I[o+4>>2]=se[1],a.getdents&&0===i&&0===n&&(a.getdents=null),0)}catch(e){return void 0!==ge&&e instanceof ge.ErrnoError||te(e),e.errno}},j:function(e,r,t,n){try{var o=ye.getStreamFromFD(e),a=ye.doWritev(o,r,t);return I[n>>2]=a,0}catch(e){return void 0!==ge&&e instanceof ge.ErrnoError||te(e),e.errno}},E:function(e){var r=Date.now();return I[e>>2]=r/1e3|0,I[e+4>>2]=r%1e3*1e3|0,0},g:function(e){}};!function(){var e={a:Gr};function r(e,r){var n,o,a=e.exports;t.asm=a,b=t.asm.I,n=b.buffer,x=n,t.HEAP8=$=new Int8Array(n),t.HEAP16=M=new Int16Array(n),t.HEAP32=I=new Int32Array(n),t.HEAPU8=R=new Uint8Array(n),t.HEAPU16=O=new Uint16Array(n),t.HEAPU32=j=new Uint32Array(n),t.HEAPF32=U=new Float32Array(n),t.HEAPF64=N=new Float64Array(n),B=t.asm.M,o=t.asm.J,J.unshift(o),re()}function n(e){r(e.instance)}function o(r){return function(){if(!E&&(s||u)){if("function"==typeof fetch&&!ae(ue))return fetch(ue,{credentials:"same-origin"}).then((function(e){if(!e.ok)throw"failed to load wasm binary file at '"+ue+"'";return e.arrayBuffer()})).catch((function(){return ce(ue)}));if(p)return new Promise((function(e,r){p(ue,(function(r){e(new Uint8Array(r))}),r)}))}return Promise.resolve().then((function(){return ce(ue)}))}().then((function(r){return WebAssembly.instantiate(r,e)})).then(r,(function(e){w("failed to asynchronously prepare wasm: "+e),te(e)}))}if(ee(),t.instantiateWasm)try{return t.instantiateWasm(e,r)}catch(e){return w("Module.instantiateWasm callback failed with error: "+e),!1}E||"function"!=typeof WebAssembly.instantiateStreaming||oe(ue)||ae(ue)||"function"!=typeof fetch?o(n):fetch(ue,{credentials:"same-origin"}).then((function(r){return WebAssembly.instantiateStreaming(r,e).then(n,(function(e){return w("wasm streaming compile failed: "+e),w("falling back to ArrayBuffer instantiation"),o(n)}))}))}(),t.___wasm_call_ctors=function(){return(t.___wasm_call_ctors=t.asm.J).apply(null,arguments)};var qr,Jr=t._free=function(){return(Jr=t._free=t.asm.K).apply(null,arguments)},Yr=t._malloc=function(){return(Yr=t._malloc=t.asm.L).apply(null,arguments)},Kr=t.___errno_location=function(){return(Kr=t.___errno_location=t.asm.N).apply(null,arguments)},Qr=t._strlen=function(){return(Qr=t._strlen=t.asm.O).apply(null,arguments)},Zr=t.___getTypeName=function(){return(Zr=t.___getTypeName=t.asm.P).apply(null,arguments)};function et(e){this.name="ExitStatus",this.message="Program terminated with exit("+e+")",this.status=e}function rt(e){function r(){qr||(qr=!0,t.calledRun=!0,k||(t.noFSInit||ge.init.initialized||ge.init(),fe(J),ge.ignorePermissions=!1,fe(Y),t.onRuntimeInitialized&&t.onRuntimeInitialized(),function(){if(t.postRun)for("function"==typeof t.postRun&&(t.postRun=[t.postRun]);t.postRun.length;)e=t.postRun.shift(),K.unshift(e);var e;fe(K)}()))}Q>0||(!function(){if(t.preRun)for("function"==typeof t.preRun&&(t.preRun=[t.preRun]);t.preRun.length;)e=t.preRun.shift(),q.unshift(e);var e;fe(q)}(),Q>0||(t.setStatus?(t.setStatus("Running..."),setTimeout((function(){setTimeout((function(){t.setStatus("")}),1),r()}),1)):r()))}if(t.___embind_register_native_and_builtin_types=function(){return(t.___embind_register_native_and_builtin_types=t.asm.Q).apply(null,arguments)},t.dynCall_ijiii=function(){return(t.dynCall_ijiii=t.asm.R).apply(null,arguments)},t.dynCall_viiijj=function(){return(t.dynCall_viiijj=t.asm.S).apply(null,arguments)},t.dynCall_jij=function(){return(t.dynCall_jij=t.asm.T).apply(null,arguments)},t.dynCall_jii=function(){return(t.dynCall_jii=t.asm.U).apply(null,arguments)},t.dynCall_jiji=function(){return(t.dynCall_jiji=t.asm.V).apply(null,arguments)},t._ff_h264_cabac_tables=82789,Z=function e(){qr||rt(),qr||(Z=e)},t.run=rt,t.preInit)for("function"==typeof t.preInit&&(t.preInit=[t.preInit]);t.preInit.length>0;)t.preInit.pop()();rt(),e.exports=t}));const u=1e3,c=!1,f=!0,l=!1,d=!1,p="initVideo",h="render",m="playAudio",v="initAudio",g="audioCode",y="videoCode",w=1,E=2,b="init",_="decode",k="audioDecode",T="videoDecode",P="close",C="updateConfig",A="key",D="delta";(()=>{try{if("object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate){const e=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));if(e instanceof WebAssembly.Module)return new WebAssembly.Instance(e)instanceof WebAssembly.Instance}}catch(e){}})(),Date.now||(Date.now=function(){return(new Date).getTime()}),s.postRun=function(){var e=[],r=[],t={};"VideoEncoder"in self&&(t={hasInit:!1,isEmitInfo:!1,offscreenCanvas:null,offscreenCanvasCtx:null,decoder:new VideoDecoder({output:function(e){t.isEmitInfo||(n.opt.debug&&console.log("Jessibuca: [worker] Webcodecs Video Decoder initSize"),postMessage({cmd:p,w:e.codedWidth,h:e.codedHeight}),t.isEmitInfo=!0,t.offscreenCanvas=new OffscreenCanvas(e.codedWidth,e.codedHeight),t.offscreenCanvasCtx=t.offscreenCanvas.getContext("2d")),t.offscreenCanvasCtx.drawImage(e,0,0,e.codedWidth,e.codedHeight);let r=t.offscreenCanvas.transferToImageBitmap();postMessage({cmd:h,buffer:r,delay:n.delay,ts:0},[r]),setTimeout((function(){e.close?e.close():e.destroy()}),100)},error:function(e){console.error(e)}}),decode:function(e,r){const o=e[0]>>4==1;if(t.hasInit){const n=new EncodedVideoChunk({data:e.slice(5),timestamp:r,type:o?A:D});t.decoder.decode(n)}else if(o&&0===e[1]){const r=15&e[0];n.setVideoCodec(r);const o=function(e){let r=e.subarray(1,4),t="avc1.";for(let e=0;e<3;e++){let n=r[e].toString(16);n.length<2&&(n="0"+n),t+=n}return{codec:t,description:e}}(e.slice(5));t.decoder.configure(o),t.hasInit=!0}},reset(){t.hasInit=!1,t.isEmitInfo=!1,t.offscreenCanvas=null,t.offscreenCanvasCtx=null}});var n={opt:{debug:c,forceNoOffscreen:f,useWCS:l,videoBuffer:u,openWebglAlignment:d},useOffscreen:function(){return!n.opt.forceNoOffscreen&&"undefined"!=typeof OffscreenCanvas},initAudioPlanar:function(e,t){postMessage({cmd:v,sampleRate:t,channels:e});var n=[],o=0;this.playAudioPlanar=function(t,a,i){for(var u=a,c=[],f=0,l=0;l<2;l++){var d=s.HEAPU32[(t>>2)+l]>>2;c[l]=s.HEAPF32.subarray(d,d+u)}if(o){if(!(u>=(a=1024-o)))return o+=u,r[0]=Float32Array.of(...r[0],...c[0]),void(2==e&&(r[1]=Float32Array.of(...r[1],...c[1])));n[0]=Float32Array.of(...r[0],...c[0].subarray(0,a)),2==e&&(n[1]=Float32Array.of(...r[1],...c[1].subarray(0,a))),postMessage({cmd:m,buffer:n,ts:i},n.map((e=>e.buffer))),f=a,u-=a}for(o=u;o>=1024;o-=1024)n[0]=c[0].slice(f,f+=1024),2==e&&(n[1]=c[1].slice(f-1024,f)),postMessage({cmd:m,buffer:n,ts:i},n.map((e=>e.buffer)));o&&(r[0]=c[0].slice(f),2==e&&(r[1]=c[1].slice(f)))}},setVideoCodec:function(e){postMessage({cmd:y,code:e})},setAudioCodec:function(e){postMessage({cmd:g,code:e})},setVideoSize:function(e,r){postMessage({cmd:p,w:e,h:r});var t=e*r,o=t>>2;n.useOffscreen()?(this.offscreenCanvas=new OffscreenCanvas(e,r),this.offscreenCanvasGL=this.offscreenCanvas.getContext("webgl"),this.webglObj=((e,r)=>{var t=["attribute vec4 vertexPos;","attribute vec4 texturePos;","varying vec2 textureCoord;","void main()","{","gl_Position = vertexPos;","textureCoord = texturePos.xy;","}"].join("\n"),n=["precision highp float;","varying highp vec2 textureCoord;","uniform sampler2D ySampler;","uniform sampler2D uSampler;","uniform sampler2D vSampler;","const mat4 YUV2RGB = mat4","(","1.1643828125, 0, 1.59602734375, -.87078515625,","1.1643828125, -.39176171875, -.81296875, .52959375,","1.1643828125, 2.017234375, 0, -1.081390625,","0, 0, 0, 1",");","void main(void) {","highp float y = texture2D(ySampler, textureCoord).r;","highp float u = texture2D(uSampler, textureCoord).r;","highp float v = texture2D(vSampler, textureCoord).r;","gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;","}"].join("\n");r&&e.pixelStorei(e.UNPACK_ALIGNMENT,1);var o=e.createShader(e.VERTEX_SHADER);e.shaderSource(o,t),e.compileShader(o),e.getShaderParameter(o,e.COMPILE_STATUS)||console.log("Vertex shader failed to compile: "+e.getShaderInfoLog(o));var a=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(a,n),e.compileShader(a),e.getShaderParameter(a,e.COMPILE_STATUS)||console.log("Fragment shader failed to compile: "+e.getShaderInfoLog(a));var i=e.createProgram();e.attachShader(i,o),e.attachShader(i,a),e.linkProgram(i),e.getProgramParameter(i,e.LINK_STATUS)||console.log("Program failed to compile: "+e.getProgramInfoLog(i)),e.useProgram(i);var s=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,s),e.bufferData(e.ARRAY_BUFFER,new Float32Array([1,1,-1,1,1,-1,-1,-1]),e.STATIC_DRAW);var u=e.getAttribLocation(i,"vertexPos");e.enableVertexAttribArray(u),e.vertexAttribPointer(u,2,e.FLOAT,!1,0,0);var c=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,c),e.bufferData(e.ARRAY_BUFFER,new Float32Array([1,0,0,0,1,1,0,1]),e.STATIC_DRAW);var f=e.getAttribLocation(i,"texturePos");function l(r,t){var n=e.createTexture();return e.bindTexture(e.TEXTURE_2D,n),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.bindTexture(e.TEXTURE_2D,null),e.uniform1i(e.getUniformLocation(i,r),t),n}e.enableVertexAttribArray(f),e.vertexAttribPointer(f,2,e.FLOAT,!1,0,0);var d=l("ySampler",0),p=l("uSampler",1),h=l("vSampler",2);return{render:function(r,t,n,o,a){e.viewport(0,0,r,t),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,d),e.texImage2D(e.TEXTURE_2D,0,e.LUMINANCE,r,t,0,e.LUMINANCE,e.UNSIGNED_BYTE,n),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,p),e.texImage2D(e.TEXTURE_2D,0,e.LUMINANCE,r/2,t/2,0,e.LUMINANCE,e.UNSIGNED_BYTE,o),e.activeTexture(e.TEXTURE2),e.bindTexture(e.TEXTURE_2D,h),e.texImage2D(e.TEXTURE_2D,0,e.LUMINANCE,r/2,t/2,0,e.LUMINANCE,e.UNSIGNED_BYTE,a),e.drawArrays(e.TRIANGLE_STRIP,0,4)},destroy:function(){try{e.deleteProgram(i),e.deleteBuffer(s),e.deleteBuffer(c),e.deleteTexture(d),e.deleteTexture(p),e.deleteBuffer(h)}catch(e){}}}})(this.offscreenCanvasGL,n.opt.openWebglAlignment),this.draw=function(n,a,i,u){this.webglObj.render(e,r,s.HEAPU8.subarray(a,a+t),s.HEAPU8.subarray(i,i+o),s.HEAPU8.subarray(u,u+o));let c=this.offscreenCanvas.transferToImageBitmap();postMessage({cmd:h,buffer:c,delay:this.delay,ts:n},[c])}):this.draw=function(e,r,n,a){var i=[s.HEAPU8.subarray(r,r+t),s.HEAPU8.subarray(n,n+o),s.HEAPU8.subarray(a,a+o)].map((e=>Uint8Array.from(e)));postMessage({cmd:h,output:i,delay:this.delay,ts:e},i.map((e=>e.buffer)))}},getDelay:function(e){return e?(this.firstTimestamp?e&&(this.delay=Date.now()-this.startTimestamp-(e-this.firstTimestamp)):(this.firstTimestamp=e,this.startTimestamp=Date.now(),this.delay=-1),this.delay):-1},resetDelay:function(){this.firstTimestamp=null,this.startTimestamp=null,this.delay=-1},init:function(){n.opt.debug&&console.log("Jessibuca: [worker] init");const r=e=>{n.opt.useWCS&&n.useOffscreen()&&e.type===E&&t.decode?t.decode(e.payload,e.ts):e.decoder.decode(e.payload,e.ts)};this.stopId=setInterval((()=>{if(e.length)if(this.dropping){for((t=e.shift()).type===w&&0===t.payload[1]&&r(t);!t.isIFrame&&e.length;)(t=e.shift()).type===w&&0===t.payload[1]&&r(t);t.isIFrame&&(this.dropping=!1,r(t))}else{var t=e[0];if(-1===this.getDelay(t.ts))n.opt.debug&&console.log("Jessibuca: [worker]: common dumex delay is -1"),e.shift(),r(t);else if(this.delay>n.opt.videoBuffer+1e3)n.opt.debug&&console.log("Jessibuca: [worker]:",`delay is ${this.delay}, set dropping is true`),this.resetDelay(),this.dropping=!0;else for(;e.length&&(t=e[0],this.getDelay(t.ts)>n.opt.videoBuffer);)e.shift(),r(t)}}),10)},close:function(){n.opt.debug&&console.log("Jessibuca: [worker]: close"),clearInterval(this.stopId),this.stopId=null,o.clear(),a.clear(),t.reset&&t.reset(),this.firstTimestamp=null,this.startTimestamp=null,this.delay=-1,this.dropping=!1,this.webglObj&&(this.webglObj.destroy(),this.offscreenCanvas=null,this.offscreenCanvasGL=null,this.offscreenCanvasCtx=null),e=[],r=[],delete this.playAudioPlanar,delete this.draw},pushBuffer:function(r,t){t.type===w?e.push({ts:t.ts,payload:r,decoder:o,type:w}):t.type===E&&e.push({ts:t.ts,payload:r,decoder:a,type:E,isIFrame:t.isIFrame})}},o=new s.AudioDecoder(n),a=new s.VideoDecoder(n);postMessage({cmd:b}),self.onmessage=function(e){var r=e.data;switch(r.cmd){case b:try{n.opt=Object.assign(n.opt,JSON.parse(r.opt))}catch(e){}o.sample_rate=r.sampleRate,n.init();break;case _:n.pushBuffer(r.buffer,r.options);break;case k:o.decode(r.buffer,r.ts);break;case T:a.decode(r.buffer,r.ts);break;case P:n.close();break;case C:n.opt[r.key]=r.value}}}})); diff --git a/web_src/static/js/jessibuca/decoder.wasm b/web_src/static/js/jessibuca/decoder.wasm new file mode 100644 index 00000000..f7a09a44 Binary files /dev/null and b/web_src/static/js/jessibuca/decoder.wasm differ diff --git a/web_src/static/js/jessibuca/ff.js b/web_src/static/js/jessibuca/ff.js deleted file mode 100644 index 62625619..00000000 --- a/web_src/static/js/jessibuca/ff.js +++ /dev/null @@ -1 +0,0 @@ -var Module=typeof Module!=="undefined"?Module:{};var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}function warnOnce(text){if(!warnOnce.shown)warnOnce.shown={};if(!warnOnce.shown[text]){warnOnce.shown[text]=1;err(text)}}var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||67108864;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile="ff.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["J"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["N"];addOnInit(Module["asm"]["K"]);removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){var result=WebAssembly.instantiate(binary,info);return result}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiationResult,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiationResult)})})}else{return instantiateArrayBuffer(receiveInstantiationResult)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync();return{}}var tempDouble;var tempI64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function jsStackTrace(){var error=new Error;if(!error.stack){try{throw new Error}catch(e){error=e}if(!error.stack){return"(no stack trace available)"}}return error.stack.toString()}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto==="object"&&typeof crypto["getRandomValues"]==="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){var alignedSize=alignMemory(size,65536);var ptr=_malloc(alignedSize);while(size=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=varargs?SYSCALLS.get():0;var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function __embind_register_bigint(primitiveType,name,size,minRange,maxRange){}function getShiftFromSize(size){switch(size){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+size)}}function embind_init_charCodes(){var codes=new Array(256);for(var i=0;i<256;++i){codes[i]=String.fromCharCode(i)}embind_charCodes=codes}var embind_charCodes=undefined;function readLatin1String(ptr){var ret="";var c=ptr;while(HEAPU8[c]){ret+=embind_charCodes[HEAPU8[c++]]}return ret}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var BindingError=undefined;function throwBindingError(message){throw new BindingError(message)}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){var f=Module["dynCall_"+sig];return args&&args.length?f.apply(null,[ptr].concat(args)):f.call(null,ptr)}function dynCall(sig,ptr,args){if(sig.includes("j")){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i>2)+i])}return array}function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.includes("unsigned");registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}var emval_methodCallers=[];function __emval_call_void_method(caller,handle,methodName,args){caller=emval_methodCallers[caller];handle=requireHandle(handle);methodName=getStringOrSymbol(methodName);caller(handle,methodName,null,args)}function __emval_addMethodCaller(caller){var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i>2)+i],"parameter "+i)}return a}function __emval_get_method_caller(argCount,argTypes){var types=__emval_lookupTypes(argCount,argTypes);var retType=types[0];var signatureName=retType.name+"_$"+types.slice(1).map(function(t){return t.name}).join("_")+"$";var params=["retType"];var args=[retType];var argsList="";for(var i=0;i4){emval_handle_array[handle].refcount+=1}}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function __emval_take_value(type,argv){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](argv);return __emval_register(v)}function _abort(){abort()}function _clock(){if(_clock.start===undefined)_clock.start=Date.now();return(Date.now()-_clock.start)*(1e6/1e3)|0}function reallyNegative(x){return x<0||x===0&&1/x===-Infinity}function convertI32PairToI53(lo,hi){return(lo>>>0)+hi*4294967296}function convertU32PairToI53(lo,hi){return(lo>>>0)+(hi>>>0)*4294967296}function reSign(value,bits){if(value<=0){return value}var half=bits<=32?Math.abs(1<=half&&(bits<=32||value>half)){value=-2*half+value}return value}function unSign(value,bits){if(value>=0){return value}return bits<=32?2*Math.abs(1<>3];argIndex+=8}else if(type=="i64"){ret=[HEAP32[argIndex>>2],HEAP32[argIndex+4>>2]];argIndex+=8}else{type="i32";ret=HEAP32[argIndex>>2];argIndex+=4}return ret}var ret=[];var curr,next,currArg;while(1){var startTextIndex=textIndex;curr=HEAP8[textIndex>>0];if(curr===0)break;next=HEAP8[textIndex+1>>0];if(curr==37){var flagAlwaysSigned=false;var flagLeftAlign=false;var flagAlternative=false;var flagZeroPad=false;var flagPadSign=false;flagsLoop:while(1){switch(next){case 43:flagAlwaysSigned=true;break;case 45:flagLeftAlign=true;break;case 35:flagAlternative=true;break;case 48:if(flagZeroPad){break flagsLoop}else{flagZeroPad=true;break}case 32:flagPadSign=true;break;default:break flagsLoop}textIndex++;next=HEAP8[textIndex+1>>0]}var width=0;if(next==42){width=getNextArg("i32");textIndex++;next=HEAP8[textIndex+1>>0]}else{while(next>=48&&next<=57){width=width*10+(next-48);textIndex++;next=HEAP8[textIndex+1>>0]}}var precisionSet=false,precision=-1;if(next==46){precision=0;precisionSet=true;textIndex++;next=HEAP8[textIndex+1>>0];if(next==42){precision=getNextArg("i32");textIndex++}else{while(1){var precisionChr=HEAP8[textIndex+1>>0];if(precisionChr<48||precisionChr>57)break;precision=precision*10+(precisionChr-48);textIndex++}}next=HEAP8[textIndex+1>>0]}if(precision<0){precision=6;precisionSet=false}var argSize;switch(String.fromCharCode(next)){case"h":var nextNext=HEAP8[textIndex+2>>0];if(nextNext==104){textIndex++;argSize=1}else{argSize=2}break;case"l":var nextNext=HEAP8[textIndex+2>>0];if(nextNext==108){textIndex++;argSize=8}else{argSize=4}break;case"L":case"q":case"j":argSize=8;break;case"z":case"t":case"I":argSize=4;break;default:argSize=null}if(argSize)textIndex++;next=HEAP8[textIndex+1>>0];switch(String.fromCharCode(next)){case"d":case"i":case"u":case"o":case"x":case"X":case"p":{var signed=next==100||next==105;argSize=argSize||4;currArg=getNextArg("i"+argSize*8);var argText;if(argSize==8){currArg=next==117?convertU32PairToI53(currArg[0],currArg[1]):convertI32PairToI53(currArg[0],currArg[1])}if(argSize<=4){var limit=Math.pow(256,argSize)-1;currArg=(signed?reSign:unSign)(currArg&limit,argSize*8)}var currAbsArg=Math.abs(currArg);var prefix="";if(next==100||next==105){argText=reSign(currArg,8*argSize,1).toString(10)}else if(next==117){argText=unSign(currArg,8*argSize,1).toString(10);currArg=Math.abs(currArg)}else if(next==111){argText=(flagAlternative?"0":"")+currAbsArg.toString(8)}else if(next==120||next==88){prefix=flagAlternative&&currArg!=0?"0x":"";if(currArg<0){currArg=-currArg;argText=(currAbsArg-1).toString(16);var buffer=[];for(var i=0;i=0){if(flagAlwaysSigned){prefix="+"+prefix}else if(flagPadSign){prefix=" "+prefix}}if(argText.charAt(0)=="-"){prefix="-"+prefix;argText=argText.substr(1)}while(prefix.length+argText.lengthexponent&&exponent>=-4){next=(next==103?"f":"F").charCodeAt(0);precision-=exponent+1}else{next=(next==103?"e":"E").charCodeAt(0);precision--}effectivePrecision=Math.min(precision,20)}if(next==101||next==69){argText=currArg.toExponential(effectivePrecision);if(/[eE][-+]\d$/.test(argText)){argText=argText.slice(0,-1)+"0"+argText.slice(-1)}}else if(next==102||next==70){argText=currArg.toFixed(effectivePrecision);if(currArg===0&&reallyNegative(currArg)){argText="-"+argText}}var parts=argText.split("e");if(isGeneral&&!flagAlternative){while(parts[0].length>1&&parts[0].includes(".")&&(parts[0].slice(-1)=="0"||parts[0].slice(-1)==".")){parts[0]=parts[0].slice(0,-1)}}else{if(flagAlternative&&argText.indexOf(".")==-1)parts[0]+=".";while(precision>effectivePrecision++)parts[0]+="0"}argText=parts[0]+(parts.length>1?"e"+parts[1]:"");if(next==69)argText=argText.toUpperCase();if(currArg>=0){if(flagAlwaysSigned){argText="+"+argText}else if(flagPadSign){argText=" "+argText}}}while(argText.length>0])}}else{ret=ret.concat(intArrayFromString("(null)".substr(0,argLength),true))}if(flagLeftAlign){while(argLength0){ret.push(32)}if(!flagLeftAlign)ret.push(getNextArg("i8"));break}case"n":{var ptr=getNextArg("i32*");HEAP32[ptr>>2]=ret.length;break}case"%":{ret.push(curr);break}default:{for(var i=startTextIndex;i>0])}}}textIndex+=2}else{ret.push(curr);textIndex+=1}}return ret}function traverseStack(args){if(!args||!args.callee||!args.callee.name){return[null,"",""]}var funstr=args.callee.toString();var funcname=args.callee.name;var str="(";var first=true;for(var i in args){var a=args[i];if(!first){str+=", "}first=false;if(typeof a==="number"||typeof a==="string"){str+=a}else{str+="("+typeof a+")"}}str+=")";var caller=args.callee.caller;args=caller?caller.arguments:[];if(first)str="";return[args,funcname,str]}function _emscripten_get_callstack_js(flags){var callstack=jsStackTrace();var iThisFunc=callstack.lastIndexOf("_emscripten_log");var iThisFunc2=callstack.lastIndexOf("_emscripten_get_callstack");var iNextLine=callstack.indexOf("\n",Math.max(iThisFunc,iThisFunc2))+1;callstack=callstack.slice(iNextLine);if(flags&32){warnOnce("EM_LOG_DEMANGLE is deprecated; ignoring")}if(flags&8&&typeof emscripten_source_map==="undefined"){warnOnce('Source map information is not available, emscripten_log with EM_LOG_C_STACK will be ignored. Build with "--pre-js $EMSCRIPTEN/src/emscripten-source-map.min.js" linker flag to add source map loading to code.');flags^=8;flags|=16}var stack_args=null;if(flags&128){stack_args=traverseStack(arguments);while(stack_args[1].includes("_emscripten_"))stack_args=traverseStack(stack_args[0])}var lines=callstack.split("\n");callstack="";var newFirefoxRe=new RegExp("\\s*(.*?)@(.*?):([0-9]+):([0-9]+)");var firefoxRe=new RegExp("\\s*(.*?)@(.*):(.*)(:(.*))?");var chromeRe=new RegExp("\\s*at (.*?) \\((.*):(.*):(.*)\\)");for(var l in lines){var line=lines[l];var symbolName="";var file="";var lineno=0;var column=0;var parts=chromeRe.exec(line);if(parts&&parts.length==5){symbolName=parts[1];file=parts[2];lineno=parts[3];column=parts[4]}else{parts=newFirefoxRe.exec(line);if(!parts)parts=firefoxRe.exec(line);if(parts&&parts.length>=4){symbolName=parts[1];file=parts[2];lineno=parts[3];column=parts[4]|0}else{callstack+=line+"\n";continue}}var haveSourceMap=false;if(flags&8){var orig=emscripten_source_map.originalPositionFor({line:lineno,column:column});haveSourceMap=orig&&orig.source;if(haveSourceMap){if(flags&64){orig.source=orig.source.substring(orig.source.replace(/\\/g,"/").lastIndexOf("/")+1)}callstack+=" at "+symbolName+" ("+orig.source+":"+orig.line+":"+orig.column+")\n"}}if(flags&16||!haveSourceMap){if(flags&64){file=file.substring(file.replace(/\\/g,"/").lastIndexOf("/")+1)}callstack+=(haveSourceMap?" = "+symbolName:" at "+symbolName)+" ("+file+":"+lineno+":"+column+")\n"}if(flags&128&&stack_args[0]){if(stack_args[1]==symbolName&&stack_args[2].length>0){callstack=callstack.replace(/\s+$/,"");callstack+=" with values: "+stack_args[1]+stack_args[2]+"\n"}stack_args=traverseStack(stack_args[0])}}callstack=callstack.replace(/\s+$/,"");return callstack}function _emscripten_log_js(flags,str){if(flags&24){str=str.replace(/\s+$/,"");str+=(str.length>0?"\n":"")+_emscripten_get_callstack_js(flags)}if(flags&1){if(flags&4){console.error(str)}else if(flags&2){console.warn(str)}else if(flags&512){console.info(str)}else if(flags&256){console.debug(str)}else{console.log(str)}}else if(flags&6){err(str)}else{out(str)}}function _emscripten_log(flags,format,varargs){var result=formatString(format,varargs);var str=UTF8ArrayToString(result,0);_emscripten_log_js(flags,str)}function abortOnCannotGrowMemory(requestedSize){abort("OOM")}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;requestedSize=requestedSize>>>0;abortOnCannotGrowMemory(requestedSize)}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator==="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){try{var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _environ_sizes_get(penviron_count,penviron_buf_size){try{var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4;HEAP8[pbuf>>0]=type;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _gettimeofday(ptr){var now=Date.now();HEAP32[ptr>>2]=now/1e3|0;HEAP32[ptr+4>>2]=now%1e3*1e3|0;return 0}function _setTempRet0(val){setTempRet0(val)}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");InternalError=Module["InternalError"]=extendError(Error,"InternalError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"z":___sys_fcntl64,"y":___sys_open,"t":__embind_register_bigint,"D":__embind_register_bool,"o":__embind_register_class,"j":__embind_register_class_constructor,"g":__embind_register_class_function,"I":__embind_register_class_property,"C":__embind_register_emval,"m":__embind_register_float,"c":__embind_register_integer,"b":__embind_register_memory_view,"n":__embind_register_std_string,"i":__embind_register_std_wstring,"E":__embind_register_void,"r":__emval_as,"e":__emval_call_void_method,"p":__emval_decref,"d":__emval_get_method_caller,"H":__emval_incref,"q":__emval_run_destructors,"w":__emval_take_value,"a":_abort,"G":_clock,"f":_emscripten_log,"u":_emscripten_resize_heap,"v":_environ_get,"x":_environ_sizes_get,"l":_fd_close,"A":_fd_fdstat_get,"B":_fd_read,"s":_fd_seek,"k":_fd_write,"F":_gettimeofday,"h":_setTempRet0};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["K"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["L"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["M"]).apply(null,arguments)};var _strlen=Module["_strlen"]=function(){return(_strlen=Module["_strlen"]=Module["asm"]["O"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["P"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["Q"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["R"]).apply(null,arguments)};var dynCall_ijiii=Module["dynCall_ijiii"]=function(){return(dynCall_ijiii=Module["dynCall_ijiii"]=Module["asm"]["S"]).apply(null,arguments)};var dynCall_viiijj=Module["dynCall_viiijj"]=function(){return(dynCall_viiijj=Module["dynCall_viiijj"]=Module["asm"]["T"]).apply(null,arguments)};var dynCall_jij=Module["dynCall_jij"]=function(){return(dynCall_jij=Module["dynCall_jij"]=Module["asm"]["U"]).apply(null,arguments)};var dynCall_jii=Module["dynCall_jii"]=function(){return(dynCall_jii=Module["dynCall_jii"]=Module["asm"]["V"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["W"]).apply(null,arguments)};var _ff_h264_cabac_tables=Module["_ff_h264_cabac_tables"]=77157;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();module.exports=Module; diff --git a/web_src/static/js/jessibuca/ff.wasm b/web_src/static/js/jessibuca/ff.wasm deleted file mode 100755 index 9b2d12d2..00000000 Binary files a/web_src/static/js/jessibuca/ff.wasm and /dev/null differ diff --git a/web_src/static/js/jessibuca/ff.worker.js b/web_src/static/js/jessibuca/ff.worker.js deleted file mode 100644 index 8f909e36..00000000 --- a/web_src/static/js/jessibuca/ff.worker.js +++ /dev/null @@ -1 +0,0 @@ -var initializedJS=false;var Module={};function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;this.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);Module["wasmModule"]=null;receiveInstance(instance);return instance.exports};function moduleLoaded(){}this.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob==="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}moduleLoaded()}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;Module["__emscripten_thread_init"](e.data.threadInfoStruct,0,0);var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["_emscripten_tls_init"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].setThreadStatus(Module["_pthread_self"](),1);if(!initializedJS){Module["___embind_register_native_and_builtin_types"]();initializedJS=true}try{var result=Module["invokeEntryPoint"](e.data.start_routine,e.data.arg);if(!Module["getNoExitRuntime"]())Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){if(ex instanceof Module["ExitStatus"]){if(Module["getNoExitRuntime"]()){}else{Module["PThread"].threadExit(ex.status)}}else{Module["PThread"].threadExit(-2);throw ex}}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(Module["_pthread_self"]()){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}}; diff --git a/web_src/static/js/jessibuca/index.js b/web_src/static/js/jessibuca/index.js deleted file mode 100644 index e68fc3bf..00000000 --- a/web_src/static/js/jessibuca/index.js +++ /dev/null @@ -1,3 +0,0 @@ -!function(){var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{};function e(t){return t&&t.__esModule?t.default:t}function r(t,e,r){Object.defineProperty(t,e,{get:r,enumerable:!0})}var i,n,o=!1;function s(){return o||(o=!0,n=t=>{var e=["attribute vec4 vertexPos;","attribute vec4 texturePos;","varying vec2 textureCoord;","void main()","{","gl_Position = vertexPos;","textureCoord = texturePos.xy;","}"].join("\n"),r=["precision highp float;","varying highp vec2 textureCoord;","uniform sampler2D ySampler;","uniform sampler2D uSampler;","uniform sampler2D vSampler;","const mat4 YUV2RGB = mat4","(","1.1643828125, 0, 1.59602734375, -.87078515625,","1.1643828125, -.39176171875, -.81296875, .52959375,","1.1643828125, 2.017234375, 0, -1.081390625,","0, 0, 0, 1",");","void main(void) {","highp float y = texture2D(ySampler, textureCoord).r;","highp float u = texture2D(uSampler, textureCoord).r;","highp float v = texture2D(vSampler, textureCoord).r;","gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;","}"].join("\n"),i=t.createShader(t.VERTEX_SHADER);t.shaderSource(i,e),t.compileShader(i),t.getShaderParameter(i,t.COMPILE_STATUS)||console.log("Vertex shader failed to compile: "+t.getShaderInfoLog(i));var n=t.createShader(t.FRAGMENT_SHADER);t.shaderSource(n,r),t.compileShader(n),t.getShaderParameter(n,t.COMPILE_STATUS)||console.log("Fragment shader failed to compile: "+t.getShaderInfoLog(n));var o=t.createProgram();t.attachShader(o,i),t.attachShader(o,n),t.linkProgram(o),t.getProgramParameter(o,t.LINK_STATUS)||console.log("Program failed to compile: "+t.getProgramInfoLog(o)),t.useProgram(o);var s=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,s),t.bufferData(t.ARRAY_BUFFER,new Float32Array([1,1,-1,1,1,-1,-1,-1]),t.STATIC_DRAW);var a=t.getAttribLocation(o,"vertexPos");t.enableVertexAttribArray(a),t.vertexAttribPointer(a,2,t.FLOAT,!1,0,0);var h=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,h),t.bufferData(t.ARRAY_BUFFER,new Float32Array([1,0,0,0,1,1,0,1]),t.STATIC_DRAW);var u=t.getAttribLocation(o,"texturePos");function f(e,r){var i=t.createTexture();return t.bindTexture(t.TEXTURE_2D,i),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.bindTexture(t.TEXTURE_2D,null),t.uniform1i(t.getUniformLocation(o,e),r),i}t.enableVertexAttribArray(u),t.vertexAttribPointer(u,2,t.FLOAT,!1,0,0);var l=f("ySampler",0),d=f("uSampler",1),c=f("vSampler",2);return function(e,r,i,n,o){t.viewport(0,0,e,r),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,l),t.texImage2D(t.TEXTURE_2D,0,t.LUMINANCE,e,r,0,t.LUMINANCE,t.UNSIGNED_BYTE,i),t.activeTexture(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,d),t.texImage2D(t.TEXTURE_2D,0,t.LUMINANCE,e/2,r/2,0,t.LUMINANCE,t.UNSIGNED_BYTE,n),t.activeTexture(t.TEXTURE2),t.bindTexture(t.TEXTURE_2D,c),t.texImage2D(t.TEXTURE_2D,0,t.LUMINANCE,e/2,r/2,0,t.LUMINANCE,t.UNSIGNED_BYTE,o),t.drawArrays(t.TRIANGLE_STRIP,0,4)}},r(i={},"default",(function(){return n}))),i}var a,h,u=!1;function f(){a={},s(),h=t=>{const e=document.createElement("canvas");e.style.position="absolute",e.style.top=0,e.style.left=0,t.$container.appendChild(e),t.$canvasElement=e,t.$container.style.overflow="hidden","absolute"!==t.$container.style.position&&(t.$container.style.position="relative");if(!t._supportOffscreen()){const e=(()=>{const e=t.$canvasElement;let r=null;const i=["webgl","experimental-webgl","moz-webgl","webkit-3d"];let n=0;for(;!r&&n{t._contextGL&&(t._contextGL=null),t._contextGLRender&&(t._contextGLRender=null),t._bitmaprenderer&&(t._bitmaprenderer=null)}},r(a,"default",(function(){return h}))}function l(){return u||(u=!0,f()),a}var d,c,p,m,g,v,b,y=!1;function w(){return y||(y=!0,c={videoBuffer:.5,vod:!1,isResize:!0,isFullSize:!1,isFlv:!1,debug:!1,timeout:30,supportDblclickFullscreen:!1,showBandwidth:!1,keepScreenOn:!1,isNotMute:!1,hasAudio:!0,operateBtns:{fullscreen:!1,screenshot:!1,play:!1,audio:!1},loadingText:"",background:"",decoder:"index.js",rotate:0,text:"",forceNoOffscreen:!0,hiddenAutoPause:!1},r(d={},"DEFAULT_OPTIONS",(function(){return c})),p={init:"init",initSize:"initSize",render:"render",playAudio:"playAudio",print:"print",printErr:"printErr",initAudioPlanar:"initAudioPlanar",kBps:"kBps"},r(d,"CMD_TYPE",(function(){return p})),m={close:"close",play:"play",setVideoBuffer:"setVideoBuffer",init:"init"},r(d,"POST_MESSAGE",(function(){return m})),g={fullscreen:"fullscreen",play:"play",pause:"pause",mute:"mute",load:"load",videoInfo:"videoInfo",timeUpdate:"timeUpdate",audioInfo:"audioInfo",log:"log",error:"error",kBps:"kBps",timeout:"timeout",stats:"stats",performance:"performance",record:"record",buffer:"buffer",videoFrame:"videoFrame",start:"start",metadata:"metadata"},r(d,"EVEMTS",(function(){return g})),v={empty:"empty",buffering:"buffering",full:"full"},r(d,"BUFFER_STATUS",(function(){return v})),b={download:"download",base64:"base64",blob:"blob"},r(d,"SCREENSHOT_TYPE",(function(){return b}))),d}var M,_=!1;function A(t){t.resume();const e=t.createBufferSource();e.buffer=t.createBuffer(1,1,22050),e.connect(t.destination),e.noteOn?e.noteOn(0):e.start(0)}function E(t,e){t&&(t.style.display=e?"block":"none")}function S(t=""){const e=t.split(","),r=atob(e[1]),i=e[0].replace("data:","").replace(";base64","");let n=r.length,o=new Uint8Array(n);for(;n--;)o[n]=r.charCodeAt(n);return new File([o],"file",{type:i})}function k(t,e){const r=document.createElement("a");r.download=e,r.href=URL.createObjectURL(t),r.click(),URL.revokeObjectURL(t)}function B(t){if(null==t||""===t)return"0 KB/S";let e=parseFloat(t);return e=e.toFixed(2),e+"KB/S"}function R(t){let e=0;return t>=24?e=2:t>=15&&(e=1),e}function T(t,e){Object.keys(e||{}).forEach((function(r){t.style[r]=e[r]}))}function x(){let t=document.fullscreenElement||window.webkitFullscreenElement||document.msFullscreenElement;return void 0===t&&(t=!1),!!t}function I(){}function C(){return(new Date).getTime()}function P(t){Object.keys(t||{}).forEach((e=>{"bgDom"!==e&&E(t[e],!1)}))}function D(t){E(t.pauseDom,!0),E(t.screenshotsDom,!0),E(t.fullscreenDom,!0),E(t.quietAudioDom,!0),E(t.textDom,!0),E(t.speedDom,!0),E(t.recordDom,!0),E(t.loadingDom,!1),E(t.playDom,!1),E(t.playBigDom,!1),E(t.bgDom,!1)}function L(t,e){let r=w().BUFFER_STATUS.buffering;return 0===t?r=w().BUFFER_STATUS.empty:t>=e&&(r=w().BUFFER_STATUS.full),r}function O(){return _||(_=!0,M={},w(),r(M,"audioContextUnlock",(function(){return A})),r(M,"$domToggle",(function(){return E})),r(M,"dataURLToFile",(function(){return S})),r(M,"downloadImg",(function(){return k})),r(M,"bpsSize",(function(){return B})),r(M,"fpsStatus",(function(){return R})),r(M,"setStyle",(function(){return T})),r(M,"checkFull",(function(){return x})),r(M,"noop",(function(){return I})),r(M,"now",(function(){return C})),r(M,"$hideBtns",(function(){return P})),r(M,"$initBtns",(function(){return D})),r(M,"bufferStatus",(function(){return L}))),M}var j,U,N=!1;function z(){return N||(N=!0,j={},O(),U=t=>{t._audioContext=new(window.AudioContext||window.webkitAudioContext),t._gainNode=t._audioContext.createGain(),t._audioEnabled=e=>{e?(O().audioContextUnlock(t._audioContext),t._audioEnabled=e=>{e?t._audioContext.resume():t._audioContext.suspend()},t._audioContext.resume()):t._audioContext.suspend()},t._audioEnabled(!0),t._mute=()=>{t._audioEnabled(!1),t.quieting=!0},t._cancelMute=()=>{t._audioEnabled(!0),t.quieting=!1},t._audioResume=()=>{t._cancelMute()},t._initAudioPlanar=e=>{const r=t._audioContext;if(!r)return!1;let i=[];const n=r.createScriptProcessor(1024,0,2);n.onaudioprocess=function(t){if(i.length){const r=i.shift();for(let i=0;i{n.disconnect(t._gainNode),t._gainNode.disconnect(r.destination),delete t._closeAudio,i=[]},t._gainNode.connect(r.destination),t._playAudio=t=>i.push(t)},t._destroyAudioContext=()=>{t._audioContext.close(),t._audioContext=null,t._gainNode=null}},r(j,"default",(function(){return U}))),j}var q,F,Z=!1;function H(){return Z||(Z=!0,q={},O(),F=t=>{t._resize$2=()=>t.resize(),t._handleVisibilityChange$2=()=>t._handleVisibilityChange(),t._onfullscreenchange$2=()=>t._onfullscreenchange(),t._handleWakeLock$2=()=>t._handleWakeLock(),window.addEventListener("resize",t._resize$2),window.addEventListener("fullscreenchange",t._onfullscreenchange$2),document.addEventListener("visibilitychange",t._handleVisibilityChange$2),document.addEventListener("visibilitychange",t._handleWakeLock$2),window.addEventListener("fullscreenchange",t._handleWakeLock$2),t._opt.supportDblclickFullscreen&&t.$canvasElement.addEventListener("dblclick",(()=>{t.fullscreen=!t.fullscreen}),!1),t._removeEventListener=()=>{window.removeEventListener("resize",t._resize$2),window.removeEventListener("fullscreenchange",t._onfullscreenchange$2),document.removeEventListener("visibilitychange",t._handleWakeLock$2),document.removeEventListener("visibilitychange",t._handleVisibilityChange$2),window.removeEventListener("fullscreenchange",t._handleWakeLock$2)},t.$doms.playDom&&t.$doms.playDom.addEventListener("click",(e=>{e.stopPropagation(),t._play()}),!1),t.$doms.playBigDom&&t.$doms.playBigDom.addEventListener("click",(e=>{e.stopPropagation(),t._play()}),!1),t.$doms.pauseDom&&t.$doms.pauseDom.addEventListener("click",(e=>{e.stopPropagation(),t._pause()}),!1),t.$doms.screenshotsDom&&t.$doms.screenshotsDom.addEventListener("click",(e=>{e.stopPropagation();const r=(t._opt.text||"")+""+O().now();t._screenshot(r)}),!1),t.$doms.fullscreenDom&&t.$doms.fullscreenDom.addEventListener("click",(e=>{e.stopPropagation(),t.fullscreen=!0}),!1),t.$doms.minScreenDom&&t.$doms.minScreenDom.addEventListener("click",(e=>{e.stopPropagation(),t.fullscreen=!1}),!1),t.$doms.recordDom&&t.$doms.recordDom.addEventListener("click",(e=>{e.stopPropagation(),t.recording=!0}),!1),t.$doms.recordingDom&&t.$doms.recordingDom.addEventListener("click",(e=>{e.stopPropagation(),t.recording=!1}),!1),t.$doms.quietAudioDom&&t.$doms.quietAudioDom.addEventListener("click",(e=>{e.stopPropagation(),t._cancelMute()}),!1),t.$doms.playAudioDom&&t.$doms.playAudioDom.addEventListener("click",(e=>{e.stopPropagation(),t._mute()}),!1),t._enableWakeLock()},r(q,"default",(function(){return F}))),q}var K,W,G=!1;function Y(){return G||(G=!0,K={},O(),H(),W=t=>{t._showControl=()=>{let e=!1,r=!1;return Object.keys(t._opt.operateBtns).forEach((e=>{t._opt.operateBtns[e]&&(r=!0)})),(t._opt.showBandwidth||t._opt.text||r)&&(e=!0),e};const e={},r=document.createDocumentFragment(),i=document.createElement("div"),n=document.createElement("div"),o=document.createElement("div"),s=document.createElement("div"),a=document.createElement("div"),h=document.createElement("div"),u=document.createElement("div"),f=document.createElement("div"),l=document.createElement("div"),d=document.createElement("div"),c=document.createElement("div"),p=document.createElement("div"),m=document.createElement("div"),g=document.createElement("div"),v=document.createElement("div"),b=document.createElement("div"),y=document.createElement("div"),w=document.createElement("div");m.innerText=t._opt.loadingText||"",s.innerText=t._opt.text||"",a.innerText="",h.title="播放",f.title="暂停",l.title="截屏",d.title="全屏",c.title="退出全屏",g.title="静音",v.title="取消静音",b.title="录制",y.title="取消录制";let M={position:"absolute",width:"100%",height:"100%"};t._opt.background&&(M=Object.assign({},M,{backgroundRepeat:"no-repeat",backgroundPosition:"center",backgroundSize:"100%",backgroundImage:"url('"+t._opt.background+"')"}));const _={position:"absolute",width:"100%",height:"100%",textAlign:"center",color:"#fff",display:"none",backgroundImage:"url('data:image/gif;base64,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')",backgroundRepeat:"no-repeat",backgroundPosition:"center",backgroundSize:"40px 40px"},A={position:"absolute",width:"100%",height:"100%",display:"none",background:"rgba(0,0,0,0.4)",backgroundImage:"url('data:image/png;base64,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')",backgroundRepeat:"no-repeat",backgroundPosition:"center",backgroundSize:"48px 48px",cursor:"pointer"},E={position:"absolute",top:0,height:"100%",display:"flex",alignItems:"center"},S={display:"none",position:"relative",fontSize:"13px",color:"#fff",lineHeight:"20px",marginLeft:"5px",marginRight:"5px",userSelect:"none"},k={display:"none",position:"relative",width:"16px",height:"16px",marginLeft:"8px",marginRight:"8px",backgroundRepeat:"no-repeat",backgroundPosition:"center",backgroundSize:"100%",cursor:"pointer"};O().setStyle(w,M),O().setStyle(i,{height:"38px",zIndex:11,position:"absolute",left:0,bottom:0,width:"100%",background:"rgba(0,0,0)"}),O().setStyle(p,_),O().setStyle(u,A),O().setStyle(m,{position:"absolute",width:"100%",top:"60%",textAlign:"center"}),O().setStyle(n,Object.assign({},E,{left:0})),O().setStyle(o,Object.assign({},E,{right:0})),O().setStyle(s,S),O().setStyle(a,S),O().setStyle(h,Object.assign({},k,{backgroundImage:"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAARVJREFUSMe9laEOglAUhs+5k9lJFpsJ5QWMJoNGbEY0mEy+gr6GNo0a3SiQCegMRILzGdw4hl+Cd27KxPuXb2zA/91z2YXoGRERkX4fvN3A2QxUiv4dFM3n8jZRBLbbVfd+ubJuF4xjiCyXkksueb1uSKCIZYGLBTEx8ekEoV7PkICeVgs8HiGyXoO2bUigCDM4HoPnM7bI8wwJ6Gk0sEXbLSay30Oo2TQkoGcwgFCSQMhxDAvoETEscDiQkJC4LjMz8+XyZ4HrFYWjEQqHQ1asWGWZfmdFAsVINxuw00HhbvfpydpvxWkKTqdYaRCUfUPJCdzv4Gr1uqfli0tOIAzByUT/iCrL6+84y3Bw+D6ui5Ou+jwA8FnIO++FACgAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDEtMDhUMTY6NDI6NTMrMDg6MDCKP7wnAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAw+2IEmwAAAEl0RVh0c3ZnOmJhc2UtdXJpAGZpbGU6Ly8vaG9tZS9hZG1pbi9pY29uLWZvbnQvdG1wL2ljb25fZ2Y3MDBzN2IzZncvYm9mYW5nLnN2Z8fICi0AAAAASUVORK5CYII=')"})),O().setStyle(f,Object.assign({},k,{backgroundImage:"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAHVJREFUSMftkCESwCAMBEOnCtdXVMKHeC7oInkEeQJXkRoEZWraipxZc8lsQqQZBACAlIS1oqGhhTCdu3oyxyyMcdRf79c5J7SWDBky+z4173rbJvR+VF/e/qwKqIAKqMBDgZyFzAQCoZTpxq7HLDyOrw/9b07l3z4dDnI2IAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMIo/vCcAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDEtMDhUMTY6NDI6NTMrMDg6MDD7YgSbAAAASnRFWHRzdmc6YmFzZS11cmkAZmlsZTovLy9ob21lL2FkbWluL2ljb24tZm9udC90bXAvaWNvbl9nZjcwMHM3YjNmdy96YW50aW5nLnN2ZxqNZJkAAAAASUVORK5CYII=')"})),O().setStyle(l,Object.assign({},k,{backgroundImage:"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAaxJREFUSMfNlLFOAkEQhmevAZMjR6OGRBJKsFBzdkYNpYSaWkopIOFRCBWh1ieA+ALGRgutjK0HzV2H5SX7W/zsmY3cnTEhcZovOzcz9+/s7Ir8d4OGht7fBwAgjvEri2OTl1ffSf0xAMBxRIkS1e3Se3+vcszEMe/6OqmT/aN2m1wsNu/o5YVsNHI7BgA4PCRfXzfXCwKy1RLbcXZG9nrkzc12jvT8nPU/PtatOThgAx8fuS4WyZ0de2e+T87n5OcnuVqRsxl5cpImQDnKUc7DA1fVqpimZCu+vCSjiNH9PlmpJNTQ0INBErfeafZRAakC6FWKfH9nwU7H/l6rGdqCOx3y7c3U+aOARsMMp+1vNskwTLjulB23XJL1epqA9OshIiKeJxAIoug7UyA4OuLi6Ynr52deu+NjOy4MSc9Ln8rMDpTLybBpaOjdXbJUIqdTm8a/t2fn/RSQewR24HicTLmGhnbdzcPquvYtGY3+PIR24UKBUXd35v6Sk4lN47+9NXm/FBAEedfGTjw9JYdDm76fm6+hoS8ujGAxT6L9Im7bTKeurvIEb92+AES1b6x283XSAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAwij+8JwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMPtiBJsAAABJdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL2hvbWUvYWRtaW4vaWNvbi1mb250L3RtcC9pY29uX2dmNzAwczdiM2Z3L2NhbWVyYS5zdmeyubWEAAAAAElFTkSuQmCC')"})),O().setStyle(d,Object.assign({},k,{backgroundImage:"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAALZJREFUSMftVbsORUAQVSj8DomChvh3lU5CoSVCQq2RObeYu8XG3deVoHCak81kds7Oaz3vxRcAAMwztOg6vX9d6/3XFQQC+b7iAoFhYE7Tvx9EIFAcy/ftO3MQGAQkCfM4MmeZWyajiLnvmYuCeduMAuSzvRBVYNluFHCssSgFp7Sq9ALKkjnPf9ubRtkDL27HNT3QtsY9cAjsNAVheHIKBOwD2wpxFHDbJpwmaHH2L1iWx+2BDy8RbXXtqbRBAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAwij+8JwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMPtiBJsAAABTdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL2hvbWUvYWRtaW4vaWNvbi1mb250L3RtcC9pY29uX2dmNzAwczdiM2Z3L3F1YW5waW5nenVpZGFodWEuc3ZnTBoI7AAAAABJRU5ErkJggg==')"})),O().setStyle(c,Object.assign({},k,{backgroundImage:"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAYJJREFUSMfdVbGKwkAQnQn+geAfWBixUTsVgp3YGKxSWflVNmIjARULwc5KO40ipNHWRgs/wGLniucKa+Jd5ODuuGle5u3szGRmd5bor4iIiMhuB3Sc+HXXBdp2/Lpta7v4dccRJUrUdhtNQIkSVa3C8HwG1uumg34f2OnEB+h0tF1Sv5b+YIsttpZLEhKSdhvscPi8IXFF74GJiYnHY7Cex8zMvFgkbInjmJnv98kqoO30vmhLtaRMB60WtEbDNDudgMUiKiQSzfjOMzFxoQAyCPSfw7/nQZ/PUYnpNGV6OR6BmYzJbzYIoBQCzGaRBDQvJCTdLnTLolg5HN5t6f8V1h/oUT4PrVKJWBotmEzQw+vV3J9Ow851P2/BaoX9Yfh0BrJZYKlk8uUyHOpDeLuBHwzMBJtN2PV6IPUhXK9Nf5cLMAxfluanrmGkRBggtRo03wfq66P/6CsJAnOg+f6rgfZI4BGYiYlHIx048eR6krcnq34kkj1GuVz8+jceo9+SD5A8yGh8CTq7AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAwij+8JwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMPtiBJsAAABNdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL2hvbWUvYWRtaW4vaWNvbi1mb250L3RtcC9pY29uX2dmNzAwczdiM2Z3L3p1aXhpYW9odWEuc3ZnoCFr0AAAAABJRU5ErkJggg==')"})),O().setStyle(g,Object.assign({},k,{backgroundImage:"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAR9JREFUSMfVlD0LglAYhe9VkwgNihpsjbYQf4JTS7+iuaGxpcGfJjS0NFRLk2NDi6MogafhJGRIX9yEzvJwrx/nvPd9VYh/F3LkyBuN2g3J1QoAgCQhPe/Hxq5Lo+0WlfJ9dYYAgGaTDAIyy/BUnwcwWJlhcLnZkN2ugIBAuy2kkEL2ep8F73S4kjfFcfn6cMj9KLodrWVBiXyf75tMyOOR+4MBOZ8XLXzorboA5UpnM/J0Ivd7+vX7xX2asqGpVKtFXi5sqWmypXefrfIWAACmU/JwKCoun8hu9zA0uk6u13wgirg+n7+bAcsibbt6SB3n9TQXPxwAwHJJpum7M6BcDDQa0SgMaw9QPkJNIxcLMo4ZcDz+eYDqQFLWbqxKV57EtW1WtMbmAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAwij+8JwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMS0wOFQxNjo0Mjo1MyswODowMPtiBJsAAABKdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL2hvbWUvYWRtaW4vaWNvbi1mb250L3RtcC9pY29uX2dmNzAwczdiM2Z3L2ppbmd5aW4uc3ZnIlMYaQAAAABJRU5ErkJggg==')"})),O().setStyle(v,Object.assign({},k,{backgroundImage:"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAU5JREFUSMftkzGKwlAURf9PULBQwULSCKK1bZAgNuoaFFyAC3AdZg0uQCwshWzAShEEO7Gy0soUCu9Occ3An5nMGCfdzGsO7+Xy3/03iVL/lbAAACiVIBCI77O37Vi9QCDZbEqLm03ycEBUAoHk818v7nYpul5Jz4tf8HBKYa1mcjwmbzd8rG8NFIsU7ffk8UjmcjE3XK+RtB4G2PT75GbDeblMttumfjSKMRCGLxsQCKTReE9KIJDJxDw/SmKxiOZWWh+ntrSlre2WXRAorbTSrZapip7X66kbMKtQUFBQCENznsmQ93vqBhh5r8fO85jAcsnIrcce1yV3uxgD8zl5uZgU+dGBVlrp6GbTKRPwffaDAek45Gz2/M0AAJ0OeTol+w0rFYrOZ3K1MhNJEjEAwHF4cBA8Z8B1zcXV6msv+JMR2yaHQ1LrXx/8Z+sNRxsWcwZeb6UAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDEtMDhUMTY6NDI6NTMrMDg6MDCKP7wnAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAw+2IEmwAAAEt0RVh0c3ZnOmJhc2UtdXJpAGZpbGU6Ly8vaG9tZS9hZG1pbi9pY29uLWZvbnQvdG1wL2ljb25fZ2Y3MDBzN2IzZncvc2hlbmd5aW4uc3ZnFog1MQAAAABJRU5ErkJggg==')"})),O().setStyle(b,Object.assign({},k,{backgroundImage:"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAPRJREFUSMflVDEOwjAQO0e8gr2sZYVunREbD6ISfAgmkBjpC/hBEQ+AtTWD6QAI0gBlqRfLp+TiXC5n1nXgMUCS5HBoNBqj6IOMMFwuEpsNAABl6d3HihWrOJaBsuRPkGW+c929HAxuYefb6L+R0ZgkMrJYiItCnCT1sl5Y1jwXj0bNniJNJWqujfX7LyrwJh8AYDxWgulU0dPp20IFlxoODm61kpE4VnS9/puBXyPYgH7LbKY3PhwUnUw+NdC4CdW9+71UgyZspwIBB9No3O0klktxUahyx+Pz+lYG0Xzu84lXRqTqwRQAGAzns8R223gUdxZXGcAK5Hp0ClIAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDEtMDhUMTY6NDI6NTMrMDg6MDCKP7wnAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAxLTA4VDE2OjQyOjUzKzA4OjAw+2IEmwAAAE50RVh0c3ZnOmJhc2UtdXJpAGZpbGU6Ly8vaG9tZS9hZG1pbi9pY29uLWZvbnQvdG1wL2ljb25fZ2Y3MDBzN2IzZncvbHV6aGlzaGlwaW4uc3Zn5Zd7GQAAAABJRU5ErkJggg==')"})),O().setStyle(y,Object.assign({},k,{backgroundImage:"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAAAahJREFUSMdjYBjpgBFd4NZK+f+soQYG//T+yzFuUFUl2cApjEWM/758UZvysPDn3127GBkZGBgY/v4l6ICb9xTWsRbp6/9f9W8N44Jz5xgCGI4wfGFiIttrR/5n/3/U3KyR8rj8t0RdHS5lcAv+//yXzzhZTY1ii2FAmsGZocna+maD3GnWY62tNzbJBbDOffLkxie5eJYwa2uYMhaigzb2/zyGguPH/y9mTGKYYGlJUIMiYxDjHCen/4oMDAxznJzg4k8Z/jP+l5LCCAFCQP30Y5dfXVZWDI7/zzIs8PNjNGJ4/7/r+XNKA4rkoNZ4/lj0V9TmzUxJv0J+F+jrM3YyvPq/acsWujmA2oBkB9y4LifLxhoa+teAzYFtwtWr/8sZxBj9fHxo7oCbprJ72MqOHWNgZGBkYFy1isGGoZahTFSU0hAgOhcQnfph4P7/df9T9u1jPMn4nyHmxIn/bAzLGe7GxTHsZyj+f+zpUwYGBmmG6bQsiMr+L/v/rqlJY9Njm9889fW4lGEUxXCHwAomUgH3vxBG8c+f1WWf9P98sns3oaJ4FAAAbtWqHTT84QYAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjEtMDEtMDhUMTY6MzU6MjMrMDg6MDBLHbvEAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAxLTA4VDE2OjM1OjIzKzA4OjAwOkADeAAAAE50RVh0c3ZnOmJhc2UtdXJpAGZpbGU6Ly8vaG9tZS9hZG1pbi9pY29uLWZvbnQvdG1wL2ljb25fcTM1YTFhNHBtY2MvbHV6aGlzaGlwaW4uc3Zn6xlv1QAAAABJRU5ErkJggg==')"})),p.appendChild(m),t._opt.text&&(n.appendChild(s),e.textDom=s),t._opt.showBandwidth&&(n.appendChild(a),e.speedDom=a),t._opt.operateBtns.record&&(o.appendChild(y),o.appendChild(b),e.recordingDom=y,e.recordDom=b),t._opt.operateBtns.screenshot&&(o.appendChild(l),e.screenshotsDom=l),t._opt.operateBtns.play&&(o.appendChild(h),o.appendChild(f),e.playDom=h,e.pauseDom=f),t._opt.operateBtns.audio&&(o.appendChild(v),o.appendChild(g),e.playAudioDom=v,e.quietAudioDom=g),t._opt.operateBtns.fullscreen&&(o.appendChild(d),o.appendChild(c),e.fullscreenDom=d,e.minScreenDom=c),i.appendChild(n),i.appendChild(o),r.appendChild(w),e.bgDom=w,r.appendChild(p),e.loadingDom=p,t._showControl()&&r.appendChild(i),t._opt.operateBtns.play&&(r.appendChild(u),e.playBigDom=u),t.$container.appendChild(r),t.$doms=e,t._removeContainerChild=()=>{for(;t.$container.firstChild;)t.$container.removeChild(t.$container.firstChild)},H().default(t),O().$hideBtns(t.$doms),t._opt.isNotMute||t._mute()},r(K,"default",(function(){return W}))),K}var V,$,X=!1;function J(){return X||(X=!0,V={},w(),O(),$=t=>{const e=new Worker(t._opt.decoder);e.onmessage=r=>{const i=r.data;switch(i.cmd){case w().CMD_TYPE.init:t.setBufferTime(t._opt.videoBuffer),e.postMessage({cmd:w().POST_MESSAGE.init,opt:JSON.stringify(t._opt),sampleRate:t._audioContext.sampleRate}),t._hasLoaded||(t._hasLoaded=!0,t.onLoad(),t._trigger(w().EVEMTS.load));break;case w().CMD_TYPE.initSize:t.$canvasElement.width=i.w,t.$canvasElement.height=i.h,t.onInitSize(),t._resize(),t._trigger(w().EVEMTS.videoInfo,{w:i.w,h:i.h}),t._trigger(w().EVEMTS.start),t._supportOffscreen()&&(t._bitmaprenderer=t.$canvasElement.getContext("bitmaprenderer"));break;case w().CMD_TYPE.render:t.loading&&(t.loading=!1,t.playing=!0,t._clearCheckLoading()),t.playing&&(t._supportOffscreen()?t._bitmaprenderer.transferFromImageBitmap(i.buffer):t._contextGLRender(t.$canvasElement.width,t.$canvasElement.height,i.output[0],i.output[1],i.output[2])),t._trigger(w().EVEMTS.timeUpdate,i.ts),t.onTimeUpdate(i.ts),t._updateStats({buf:i.delay,ts:i.ts}),t._checkHeart();break;case w().CMD_TYPE.playAudio:t.playing&&!t.quieting&&t._playAudio(i.buffer);break;case w().CMD_TYPE.print:t.onLog(i.text),t._trigger(w().EVEMTS.log,i.text);break;case w().CMD_TYPE.printErr:t.onLog(i.text),t._trigger(w().EVEMTS.log,i.text),t.onError(i.text),t._trigger(w().EVEMTS.error,i.text);break;case w().CMD_TYPE.initAudioPlanar:t._initAudioPlanar(i),t._trigger(w().EVEMTS.audioInfo,{numOfChannels:i.channels,sampleRate:i.samplerate});break;case w().CMD_TYPE.kBps:t.playing&&(t.$doms.speedDom&&(t.$doms.speedDom.innerText=O().bpsSize(i.kBps)),t._trigger(w().EVEMTS.kBps,i.kBps));default:t[i.cmd]&&t[i.cmd](i)}},t._decoderWorker=e},r(V,"default",(function(){return $}))),V}var Q,tt,et=!1;function rt(){return et||(et=!0,Q={},O(),w(),tt=t=>{t._loading=!0,t._recording=!1,t._playing=!1,t._audioPlaying=!1,t._quieting=!1,t._fullscreen=!1,t._stats={buf:0,fps:0,abps:"",vbps:"",ts:""},t._hasLoaded=!1,t._playUrl="",t._startBpsTime="",t._bps=0,t._checkHeartTimeout=null,t._wakeLock=null,t._contextGL=null,t._contextGLRender=null,t._checkLoadingTimeout=null,t._bitmaprenderer=null,t._isPlayingBeforePageHidden=!1,t._initCheckVariable=()=>{t._startBpsTime="",t._bps=0,t._clearCheckHeartTimeout(),t._clearCheckLoading()},t._clearCheckHeartTimeout=()=>{t._checkHeartTimeout&&(clearTimeout(t._checkHeartTimeout),t._checkHeartTimeout=null)},t._startCheckHeartTimeout=()=>{t._checkHeartTimeout=setTimeout((function(){t._trigger(w().EVEMTS.timeout),t.recording=!1,t.playing=!1,t._close()}),1e3*t._opt.timeout)},t._clearCheckLoading=()=>{t._checkLoadingTimeout&&(clearTimeout(t._checkLoadingTimeout),t._checkLoadingTimeout=null)},t._checkLoading=()=>{t._clearCheckLoading(),t._checkLoadingTimeout=setTimeout((()=>{t._trigger(w().EVEMTS.timeout),t.playing=!1,t._close(),O().$domToggle(t.$doms.loadingDom,!1)}),1e3*t._opt.timeout)}},r(Q,"default",(function(){return tt}))),Q}var it,nt,ot=!1;function st(){return ot||(ot=!0,it={},O(),nt=t=>{t.onPlay=O().noop,t.onPause=O().noop,t.onRecord=O().noop,t.onFullscreen=O().noop,t.onMute=O().noop,t.onLoad=O().noop,t.onLog=O().noop,t.onError=O().noop,t.onTimeUpdate=O().noop,t.onInitSize=O().noop},r(it,"default",(function(){return nt}))),it}var at,ht,ut=!1;function ft(){return ut||(ut=!0,ht=t=>{t._on=(e,r)=>{let i,n,o;if(!r)return t;for(i=t.__events||(t.__events={}),e=e.split(/\s+/);n=e.shift();)o=i[n]||(i[n]=[]),o.push(r);return t},t._off=()=>{let e;return(e=t.__events)?(delete t.__events,t):t},t._trigger=(e,...r)=>{function i(t,e){if(t)for(let r=0,i=t.length;r{t._pause=()=>{t._close(),t.loading&&O().$domToggle(t.$doms.loadingDom,!1),t.recording=!1,t.playing=!1},t._play=e=>{if(!t._playUrl&&!e)return;let r=!1;e?(t._playUrl&&(t._close(),r=!0,t.clearView()),t.loading=!0,O().$domToggle(t.$doms.bgDom,!1),t._checkLoading(),t._playUrl=e):t._playUrl&&(t.loading?(O().$hideBtns(t.$doms),O().$domToggle(t.$doms.fullscreenDom,!0),O().$domToggle(t.$doms.pauseDom,!0),O().$domToggle(t.$doms.loadingDom,!0),t._checkLoading()):t.playing=!0),t._initCheckVariable(),r?setTimeout((()=>{t._decoderWorker.postMessage({cmd:w().POST_MESSAGE.play,url:t._playUrl})}),300):t._decoderWorker.postMessage({cmd:w().POST_MESSAGE.play,url:t._playUrl})},t._screenshot=(e,r,i,n)=>{e=e||O().now(),n=n||w().SCREENSHOT_TYPE.download;const o={png:"image/png",jpeg:"image/jpeg",webp:"image/webp"};let s=.92;!o[r]&&w().SCREENSHOT_TYPE[r]&&(n=r,r="png",i=void 0),"string"==typeof i&&(n=i,i=void 0),void 0!==i&&(s=Number(i));const a=t.$canvasElement.toDataURL(o[r]||o.png,s),h=O().dataURLToFile(a);return n===w().SCREENSHOT_TYPE.base64?a:n===w().SCREENSHOT_TYPE.blob?h:void(n===w().SCREENSHOT_TYPE.download&&O().downloadImg(h,e))},t._close=()=>{t._close$2(),t._clearView()},t._close$2=()=>{t._opt.debug&&console.log("_close$2-START"),t._closeAudio&&t._closeAudio(),t._audioPlayBuffers=[],t._audioPlaying=!1,t._decoderWorker.postMessage({cmd:w().POST_MESSAGE.close}),delete t._playAudio,t._releaseWakeLock(),t._initCheckVariable(),t._opt.debug&&console.log("_close$2-END")},t._releaseWakeLock=()=>{t._wakeLock&&(t._wakeLock.release(),t._wakeLock=null)},t._clearView=()=>{t._contextGL&&t._contextGL.clear(t._contextGL.COLOR_BUFFER_BIT)},t._resize=()=>{let e=t.$container.clientWidth,r=t.$container.clientHeight;t._showControl()&&(r-=38);let i=t.$canvasElement.width,n=t.$canvasElement.height;const o=t._opt.rotate;let s=(e-i)/2,a=(r-n)/2;270!==o&&90!==o||(i=t.$canvasElement.height,n=t.$canvasElement.width);let h=e/i,u=r/n,f=h>u?u:h;t._opt.isResize||h!==u&&(f=h+","+u),t._opt.isFullResize&&(f=h>u?h:u);let l="scale("+f+")";o&&(l+=" rotate("+o+"deg)"),t.$canvasElement.style.transform=l,t.$canvasElement.style.left=s+"px",t.$canvasElement.style.top=a+"px"},t._enableWakeLock=()=>{t._opt.keepScreenOn&&"wakeLock"in navigator&&navigator.wakeLock.request("screen").then((e=>{t._wakeLock=e}))},t._supportOffscreen=()=>!t._opt.forceNoOffscreen&&"function"==typeof t.$canvasElement.transferControlToOffscreen,t._checkHeart=()=>{t._clearCheckHeartTimeout(),t._startCheckHeartTimeout()},t._updateStats=e=>{e=e||{},t._startBpsTime||(t._startBpsTime=O().now());const r=O().now();r-t._startBpsTime<1e3?t._stats.fps+=1:(t._stats.ts=e.ts,t._stats.buf=e.buf,t._trigger(w().EVEMTS.stats,t._stats),t._trigger(w().EVEMTS.performance,O().fpsStatus(t._stats.fps)),t._trigger(w().EVEMTS.buffer,O().bufferStatus(t._stats.buf,1e3*t._opt.videoBuffer)),t._stats.fps=0,t._startBpsTime=r)},t._onfullscreenchange=()=>{t.fullscreen=O().checkFull()},t._handleVisibilityChange=()=>{t._opt.hiddenAutoPause&&(t._opt.debug&&console.log(document.visibilityState,t._isPlayingBeforePageHidden),"visible"===document.visibilityState?t._isPlayingBeforePageHidden&&t._play():(t._isPlayingBeforePageHidden=t.playing,t.playing&&t._pause()))},t._handleWakeLock=()=>{null!==t._wakeLock&&"visible"===document.visibilityState&&t._enableWakeLock()}},r(lt,"default",(function(){return dt}))),lt}var mt,gt,vt=!1;function bt(){return vt||(vt=!0,mt={},rt(),st(),ft(),pt(),gt=t=>{rt().default(t),st().default(t),ft().default(t),pt().default(t)},r(mt,"default",(function(){return gt}))),mt}var yt,wt=!1;var Mt,_t,At,Et,St,kt,Bt,Rt,Tt=!1;function xt(){throw new Error("setTimeout has not been defined")}function It(){throw new Error("clearTimeout has not been defined")}function Ct(t){if(At===setTimeout)return setTimeout(t,0);if((At===xt||!At)&&setTimeout)return At=setTimeout,setTimeout(t,0);try{return At(t,0)}catch(e){try{return At.call(null,t,0)}catch(e){return At.call(this,t,0)}}}function Pt(){kt&&Bt&&(kt=!1,Bt.length?St=Bt.concat(St):Rt=-1,St.length&&Dt())}function Dt(){if(!kt){var t=Ct(Pt);kt=!0;for(var e=St.length;e;){for(Bt=St,St=[];++Rt1)for(var r=1;r0)throw new Error("Invalid string. Length must be a multiple of 4");var r=t.indexOf("=");return-1===r&&(r=e),[r,r===e?0:4-r%4]}function Yt(t){var e,r,i=Gt(t),n=i[0],o=i[1],s=new Ft(function(t,e,r){return 3*(e+r)/4-r}(0,n,o)),a=0,h=o>0?n-4:n;for(r=0;r>16&255,s[a++]=e>>8&255,s[a++]=255&e;return 2===o&&(e=qt[t.charCodeAt(r)]<<2|qt[t.charCodeAt(r+1)]>>4,s[a++]=255&e),1===o&&(e=qt[t.charCodeAt(r)]<<10|qt[t.charCodeAt(r+1)]<<4|qt[t.charCodeAt(r+2)]>>2,s[a++]=e>>8&255,s[a++]=255&e),s}function Vt(t,e,r){for(var i,n,o=[],s=e;s>18&63]+zt[n>>12&63]+zt[n>>6&63]+zt[63&n]);return o.join("")}function $t(t){for(var e,r=t.length,i=r%3,n=[],o=16383,s=0,a=r-i;sa?a:s+o));return 1===i?(e=t[r-1],n.push(zt[e>>2]+zt[e<<4&63]+"==")):2===i&&(e=(t[r-2]<<8)+t[r-1],n.push(zt[e>>10]+zt[e>>4&63]+zt[e<<2&63]+"=")),n.join("")}function Xt(){return Wt||(Wt=!0,function(){for((Ut={}).toByteArray=Yt,Nt=$t,Ut.fromByteArray=Nt,zt=[],qt=[],Ft="undefined"!=typeof Uint8Array?Uint8Array:Array,Ht=0,Kt=(Zt="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/").length;Ht*/ -(Jt={}).read=function(t,e,r,i,n){var o,s,a=8*n-i-1,h=(1<>1,f=-7,l=r?n-1:0,d=r?-1:1,c=t[e+l];for(l+=d,o=c&(1<<-f)-1,c>>=-f,f+=a;f>0;o=256*o+t[e+l],l+=d,f-=8);for(s=o&(1<<-f)-1,o>>=-f,f+=i;f>0;s=256*s+t[e+l],l+=d,f-=8);if(0===o)o=1-u;else{if(o===h)return s?NaN:1/0*(c?-1:1);s+=Math.pow(2,i),o-=u}return(c?-1:1)*s*Math.pow(2,o-i)},Qt=function(t,e,r,i,n,o){var s,a,h,u=8*o-n-1,f=(1<>1,d=23===n?Math.pow(2,-24)-Math.pow(2,-77):0,c=i?0:o-1,p=i?1:-1,m=e<0||0===e&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(a=isNaN(e)?1:0,s=f):(s=Math.floor(Math.log(e)/Math.LN2),e*(h=Math.pow(2,-s))<1&&(s--,h*=2),(e+=s+l>=1?d/h:d*Math.pow(2,1-l))*h>=2&&(s++,h/=2),s+l>=f?(a=0,s=f):s+l>=1?(a=(e*h-1)*Math.pow(2,n),s+=l):(a=e*Math.pow(2,l-1)*Math.pow(2,n),s=0));n>=8;t[r+c]=255&a,c+=p,a/=256,n-=8);for(s=s<0;t[r+c]=255&s,c+=p,s/=256,u-=8);t[r+c-p]|=128*m},Jt.write=Qt),Jt}var re,ie,ne,oe,se,ae,he,ue,fe,le=!1;function de(t){if(t>se)throw new RangeError('The value "'+t+'" is invalid for option "size"');var e=new Uint8Array(t);return Object.setPrototypeOf(e,ce.prototype),e}function ce(t,e,r){if("number"==typeof t){if("string"==typeof e)throw new TypeError('The "string" argument must be of type string. Received type number');return ge(t)}return pe(t,e,r)}function pe(t,e,r){if("string"==typeof t)return function(t,e){"string"==typeof e&&""!==e||(e="utf8");if(!ce.isEncoding(e))throw new TypeError("Unknown encoding: "+e);var r=0|Me(t,e),i=de(r),n=i.write(t,e);n!==r&&(i=i.slice(0,n));return i}(t,e);if(ArrayBuffer.isView(t))return function(t){if(Ke(t,Uint8Array)){var e=new Uint8Array(t);return be(e.buffer,e.byteOffset,e.byteLength)}return ve(t)}(t);if(null==t)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);if(Ke(t,ArrayBuffer)||t&&Ke(t.buffer,ArrayBuffer))return be(t,e,r);if("undefined"!=typeof SharedArrayBuffer&&(Ke(t,SharedArrayBuffer)||t&&Ke(t.buffer,SharedArrayBuffer)))return be(t,e,r);if("number"==typeof t)throw new TypeError('The "value" argument must not be of type number. Received type number');var i=t.valueOf&&t.valueOf();if(null!=i&&i!==t)return ce.from(i,e,r);var n=function(t){if(ce.isBuffer(t)){var e=0|ye(t.length),r=de(e);return 0===r.length||t.copy(r,0,0,e),r}if(void 0!==t.length)return"number"!=typeof t.length||We(t.length)?de(0):ve(t);if("Buffer"===t.type&&Array.isArray(t.data))return ve(t.data)}(t);if(n)return n;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof t[Symbol.toPrimitive])return ce.from(t[Symbol.toPrimitive]("string"),e,r);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t)}function me(t){if("number"!=typeof t)throw new TypeError('"size" argument must be of type number');if(t<0)throw new RangeError('The value "'+t+'" is invalid for option "size"')}function ge(t){return me(t),de(t<0?0:0|ye(t))}function ve(t){for(var e=t.length<0?0:0|ye(t.length),r=de(e),i=0;i=se)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+se.toString(16)+" bytes");return 0|t}function we(t){return+t!=t&&(t=0),ce.alloc(+t)}function Me(t,e){if(ce.isBuffer(t))return t.length;if(ArrayBuffer.isView(t)||Ke(t,ArrayBuffer))return t.byteLength;if("string"!=typeof t)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof t);var r=t.length,i=arguments.length>2&&!0===arguments[2];if(!i&&0===r)return 0;for(var n=!1;;)switch(e){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":return Fe(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return Ze(t).length;default:if(n)return i?-1:Fe(t).length;e=(""+e).toLowerCase(),n=!0}}function _e(t,e,r){var i=!1;if((void 0===e||e<0)&&(e=0),e>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(e>>>=0))return"";for(t||(t="utf8");;)switch(t){case"hex":return Le(this,e,r);case"utf8":case"utf-8":return Ce(this,e,r);case"ascii":return Pe(this,e,r);case"latin1":case"binary":return De(this,e,r);case"base64":return Ie(this,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return Oe(this,e,r);default:if(i)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),i=!0}}function Ae(t,e,r){var i=t[e];t[e]=t[r],t[r]=i}function Ee(t,e,r,i,n){if(0===t.length)return-1;if("string"==typeof r?(i=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),We(r=+r)&&(r=n?0:t.length-1),r<0&&(r=t.length+r),r>=t.length){if(n)return-1;r=t.length-1}else if(r<0){if(!n)return-1;r=0}if("string"==typeof e&&(e=ce.from(e,i)),ce.isBuffer(e))return 0===e.length?-1:Se(t,e,r,i,n);if("number"==typeof e)return e&=255,"function"==typeof Uint8Array.prototype.indexOf?n?Uint8Array.prototype.indexOf.call(t,e,r):Uint8Array.prototype.lastIndexOf.call(t,e,r):Se(t,[e],r,i,n);throw new TypeError("val must be string, number or Buffer")}function Se(t,e,r,i,n){var o,s=1,a=t.length,h=e.length;if(void 0!==i&&("ucs2"===(i=String(i).toLowerCase())||"ucs-2"===i||"utf16le"===i||"utf-16le"===i)){if(t.length<2||e.length<2)return-1;s=2,a/=2,h/=2,r/=2}function u(t,e){return 1===s?t[e]:t.readUInt16BE(e*s)}if(n){var f=-1;for(o=r;oa&&(r=a-h),o=r;o>=0;o--){for(var l=!0,d=0;dn&&(i=n):i=n;var o=e.length;i>o/2&&(i=o/2);for(var s=0;s>8,n=r%256,o.push(n),o.push(i);return o}(e,t.length-r),t,r,i)}function Ie(t,e,r){return 0===e&&r===t.length?Xt().fromByteArray(t):Xt().fromByteArray(t.slice(e,r))}function Ce(t,e,r){r=Math.min(t.length,r);for(var i=[],n=e;n239?4:u>223?3:u>191?2:1;if(n+l<=r)switch(l){case 1:u<128&&(f=u);break;case 2:128==(192&(o=t[n+1]))&&(h=(31&u)<<6|63&o)>127&&(f=h);break;case 3:o=t[n+1],s=t[n+2],128==(192&o)&&128==(192&s)&&(h=(15&u)<<12|(63&o)<<6|63&s)>2047&&(h<55296||h>57343)&&(f=h);break;case 4:o=t[n+1],s=t[n+2],a=t[n+3],128==(192&o)&&128==(192&s)&&128==(192&a)&&(h=(15&u)<<18|(63&o)<<12|(63&s)<<6|63&a)>65535&&h<1114112&&(f=h)}null===f?(f=65533,l=1):f>65535&&(f-=65536,i.push(f>>>10&1023|55296),f=56320|1023&f),i.push(f),n+=l}return function(t){var e=t.length;if(e<=he)return String.fromCharCode.apply(String,t);var r="",i=0;for(;ii)&&(r=i);for(var n="",o=e;or)throw new RangeError("Trying to access beyond buffer length")}function Ue(t,e,r,i,n,o){if(!ce.isBuffer(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>n||et.length)throw new RangeError("Index out of range")}function Ne(t,e,r,i,n,o){if(r+i>t.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function ze(t,e,r,i,n){return e=+e,r>>>=0,n||Ne(t,0,r,4),ee().write(t,e,r,i,23,4),r+4}function qe(t,e,r,i,n){return e=+e,r>>>=0,n||Ne(t,0,r,8),ee().write(t,e,r,i,52,8),r+8}function Fe(t,e){var r;e=e||1/0;for(var i=t.length,n=null,o=[],s=0;s55295&&r<57344){if(!n){if(r>56319){(e-=3)>-1&&o.push(239,191,189);continue}if(s+1===i){(e-=3)>-1&&o.push(239,191,189);continue}n=r;continue}if(r<56320){(e-=3)>-1&&o.push(239,191,189),n=r;continue}r=65536+(n-55296<<10|r-56320)}else n&&(e-=3)>-1&&o.push(239,191,189);if(n=null,r<128){if((e-=1)<0)break;o.push(r)}else if(r<2048){if((e-=2)<0)break;o.push(r>>6|192,63&r|128)}else if(r<65536){if((e-=3)<0)break;o.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;o.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return o}function Ze(t){return Xt().toByteArray(function(t){if((t=(t=t.split("=")[0]).trim().replace(ue,"")).length<2)return"";for(;t.length%4!=0;)t+="=";return t}(t))}function He(t,e,r,i){for(var n=0;n=e.length||n>=t.length);++n)e[n+r]=t[n];return n}function Ke(t,e){return t instanceof e||null!=t&&null!=t.constructor&&null!=t.constructor.name&&t.constructor.name===e.name}function We(t){return t!=t}function Ge(){re={},Xt(),ee(),ie="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null,ne=ce,re.Buffer=ne,oe=we,re.SlowBuffer=oe,50,re.INSPECT_MAX_BYTES=50,ae=se=2147483647,re.kMaxLength=ae,ce.TYPED_ARRAY_SUPPORT=function(){try{var t=new Uint8Array(1),e={foo:function(){return 42}};return Object.setPrototypeOf(e,Uint8Array.prototype),Object.setPrototypeOf(t,e),42===t.foo()}catch(t){return!1}}(),ce.TYPED_ARRAY_SUPPORT||"undefined"==typeof console||"function"!=typeof console.error||console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support."),Object.defineProperty(ce.prototype,"parent",{enumerable:!0,get:function(){if(ce.isBuffer(this))return this.buffer}}),Object.defineProperty(ce.prototype,"offset",{enumerable:!0,get:function(){if(ce.isBuffer(this))return this.byteOffset}}),ce.poolSize=8192,ce.from=function(t,e,r){return pe(t,e,r)},Object.setPrototypeOf(ce.prototype,Uint8Array.prototype),Object.setPrototypeOf(ce,Uint8Array),ce.alloc=function(t,e,r){return function(t,e,r){return me(t),t<=0?de(t):void 0!==e?"string"==typeof r?de(t).fill(e,r):de(t).fill(e):de(t)}(t,e,r)},ce.allocUnsafe=function(t){return ge(t)},ce.allocUnsafeSlow=function(t){return ge(t)},ce.isBuffer=function(t){return null!=t&&!0===t._isBuffer&&t!==ce.prototype},ce.compare=function(t,e){if(Ke(t,Uint8Array)&&(t=ce.from(t,t.offset,t.byteLength)),Ke(e,Uint8Array)&&(e=ce.from(e,e.offset,e.byteLength)),!ce.isBuffer(t)||!ce.isBuffer(e))throw new TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array');if(t===e)return 0;for(var r=t.length,i=e.length,n=0,o=Math.min(r,i);ni.length?ce.from(o).copy(i,n):Uint8Array.prototype.set.call(i,o,n);else{if(!ce.isBuffer(o))throw new TypeError('"list" argument must be an Array of Buffers');o.copy(i,n)}n+=o.length}return i},ce.byteLength=Me,ce.prototype._isBuffer=!0,ce.prototype.swap16=function(){var t=this.length;if(t%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(var e=0;e50&&(t+=" ... "),""},ie&&(ce.prototype[ie]=ce.prototype.inspect),ce.prototype.compare=function(t,e,r,i,n){if(Ke(t,Uint8Array)&&(t=ce.from(t,t.offset,t.byteLength)),!ce.isBuffer(t))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof t);if(void 0===e&&(e=0),void 0===r&&(r=t?t.length:0),void 0===i&&(i=0),void 0===n&&(n=this.length),e<0||r>t.length||i<0||n>this.length)throw new RangeError("out of range index");if(i>=n&&e>=r)return 0;if(i>=n)return-1;if(e>=r)return 1;if(this===t)return 0;for(var o=(n>>>=0)-(i>>>=0),s=(r>>>=0)-(e>>>=0),a=Math.min(o,s),h=this.slice(i,n),u=t.slice(e,r),f=0;f>>=0,isFinite(r)?(r>>>=0,void 0===i&&(i="utf8")):(i=r,r=void 0)}var n=this.length-e;if((void 0===r||r>n)&&(r=n),t.length>0&&(r<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");i||(i="utf8");for(var o=!1;;)switch(i){case"hex":return ke(this,t,e,r);case"utf8":case"utf-8":return Be(this,t,e,r);case"ascii":case"latin1":case"binary":return Re(this,t,e,r);case"base64":return Te(this,t,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return xe(this,t,e,r);default:if(o)throw new TypeError("Unknown encoding: "+i);i=(""+i).toLowerCase(),o=!0}},ce.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}},he=4096,ce.prototype.slice=function(t,e){var r=this.length;(t=~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),(e=void 0===e?r:~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),e>>=0,e>>>=0,r||je(t,e,this.length);for(var i=this[t],n=1,o=0;++o>>=0,e>>>=0,r||je(t,e,this.length);for(var i=this[t+--e],n=1;e>0&&(n*=256);)i+=this[t+--e]*n;return i},ce.prototype.readUint8=ce.prototype.readUInt8=function(t,e){return t>>>=0,e||je(t,1,this.length),this[t]},ce.prototype.readUint16LE=ce.prototype.readUInt16LE=function(t,e){return t>>>=0,e||je(t,2,this.length),this[t]|this[t+1]<<8},ce.prototype.readUint16BE=ce.prototype.readUInt16BE=function(t,e){return t>>>=0,e||je(t,2,this.length),this[t]<<8|this[t+1]},ce.prototype.readUint32LE=ce.prototype.readUInt32LE=function(t,e){return t>>>=0,e||je(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},ce.prototype.readUint32BE=ce.prototype.readUInt32BE=function(t,e){return t>>>=0,e||je(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},ce.prototype.readIntLE=function(t,e,r){t>>>=0,e>>>=0,r||je(t,e,this.length);for(var i=this[t],n=1,o=0;++o=(n*=128)&&(i-=Math.pow(2,8*e)),i},ce.prototype.readIntBE=function(t,e,r){t>>>=0,e>>>=0,r||je(t,e,this.length);for(var i=e,n=1,o=this[t+--i];i>0&&(n*=256);)o+=this[t+--i]*n;return o>=(n*=128)&&(o-=Math.pow(2,8*e)),o},ce.prototype.readInt8=function(t,e){return t>>>=0,e||je(t,1,this.length),128&this[t]?-1*(255-this[t]+1):this[t]},ce.prototype.readInt16LE=function(t,e){t>>>=0,e||je(t,2,this.length);var r=this[t]|this[t+1]<<8;return 32768&r?4294901760|r:r},ce.prototype.readInt16BE=function(t,e){t>>>=0,e||je(t,2,this.length);var r=this[t+1]|this[t]<<8;return 32768&r?4294901760|r:r},ce.prototype.readInt32LE=function(t,e){return t>>>=0,e||je(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},ce.prototype.readInt32BE=function(t,e){return t>>>=0,e||je(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},ce.prototype.readFloatLE=function(t,e){return t>>>=0,e||je(t,4,this.length),ee().read(this,t,!0,23,4)},ce.prototype.readFloatBE=function(t,e){return t>>>=0,e||je(t,4,this.length),ee().read(this,t,!1,23,4)},ce.prototype.readDoubleLE=function(t,e){return t>>>=0,e||je(t,8,this.length),ee().read(this,t,!0,52,8)},ce.prototype.readDoubleBE=function(t,e){return t>>>=0,e||je(t,8,this.length),ee().read(this,t,!1,52,8)},ce.prototype.writeUintLE=ce.prototype.writeUIntLE=function(t,e,r,i){(t=+t,e>>>=0,r>>>=0,i)||Ue(this,t,e,r,Math.pow(2,8*r)-1,0);var n=1,o=0;for(this[e]=255&t;++o>>=0,r>>>=0,i)||Ue(this,t,e,r,Math.pow(2,8*r)-1,0);var n=r-1,o=1;for(this[e+n]=255&t;--n>=0&&(o*=256);)this[e+n]=t/o&255;return e+r},ce.prototype.writeUint8=ce.prototype.writeUInt8=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,1,255,0),this[e]=255&t,e+1},ce.prototype.writeUint16LE=ce.prototype.writeUInt16LE=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,2,65535,0),this[e]=255&t,this[e+1]=t>>>8,e+2},ce.prototype.writeUint16BE=ce.prototype.writeUInt16BE=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,2,65535,0),this[e]=t>>>8,this[e+1]=255&t,e+2},ce.prototype.writeUint32LE=ce.prototype.writeUInt32LE=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,4,4294967295,0),this[e+3]=t>>>24,this[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t,e+4},ce.prototype.writeUint32BE=ce.prototype.writeUInt32BE=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,4,4294967295,0),this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t,e+4},ce.prototype.writeIntLE=function(t,e,r,i){if(t=+t,e>>>=0,!i){var n=Math.pow(2,8*r-1);Ue(this,t,e,r,n-1,-n)}var o=0,s=1,a=0;for(this[e]=255&t;++o>0)-a&255;return e+r},ce.prototype.writeIntBE=function(t,e,r,i){if(t=+t,e>>>=0,!i){var n=Math.pow(2,8*r-1);Ue(this,t,e,r,n-1,-n)}var o=r-1,s=1,a=0;for(this[e+o]=255&t;--o>=0&&(s*=256);)t<0&&0===a&&0!==this[e+o+1]&&(a=1),this[e+o]=(t/s>>0)-a&255;return e+r},ce.prototype.writeInt8=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,1,127,-128),t<0&&(t=255+t+1),this[e]=255&t,e+1},ce.prototype.writeInt16LE=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,2,32767,-32768),this[e]=255&t,this[e+1]=t>>>8,e+2},ce.prototype.writeInt16BE=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,2,32767,-32768),this[e]=t>>>8,this[e+1]=255&t,e+2},ce.prototype.writeInt32LE=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,4,2147483647,-2147483648),this[e]=255&t,this[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24,e+4},ce.prototype.writeInt32BE=function(t,e,r){return t=+t,e>>>=0,r||Ue(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t,e+4},ce.prototype.writeFloatLE=function(t,e,r){return ze(this,t,e,!0,r)},ce.prototype.writeFloatBE=function(t,e,r){return ze(this,t,e,!1,r)},ce.prototype.writeDoubleLE=function(t,e,r){return qe(this,t,e,!0,r)},ce.prototype.writeDoubleBE=function(t,e,r){return qe(this,t,e,!1,r)},ce.prototype.copy=function(t,e,r,i){if(!ce.isBuffer(t))throw new TypeError("argument should be a Buffer");if(r||(r=0),i||0===i||(i=this.length),e>=t.length&&(e=t.length),e||(e=0),i>0&&i=this.length)throw new RangeError("Index out of range");if(i<0)throw new RangeError("sourceEnd out of bounds");i>this.length&&(i=this.length),t.length-e>>=0,r=void 0===r?this.length:r>>>0,t||(t=0),"number"==typeof t)for(o=e;o2){var h=i.lastIndexOf("/");if(h!==i.length-1){-1===h?(i="",n=0):n=(i=i.slice(0,h)).length-1-i.lastIndexOf("/"),o=a,s=0;continue}}else if(2===i.length||1===i.length){i="",n=0,o=a,s=0;continue}e&&(i.length>0?i+="/..":i="..",n=2)}else i.length>0?i+="/"+t.slice(o+1,a):i=t.slice(o+1,a),n=a-o-1;o=a,s=0}else 46===r&&-1!==s?++s:s=-1}return i}function tr(){return Xe||(Xe=!0,Ve={},jt(),$e={resolve:function(){for(var t,e="",r=!1,i=arguments.length-1;i>=-1&&!r;i--){var n;i>=0?n=arguments[i]:(void 0===t&&(t=jt().cwd()),n=t),Je(n),0!==n.length&&(e=n+"/"+e,r=47===n.charCodeAt(0))}return e=Qe(e,!r),r?e.length>0?"/"+e:"/":e.length>0?e:"."},normalize:function(t){if(Je(t),0===t.length)return".";var e=47===t.charCodeAt(0),r=47===t.charCodeAt(t.length-1);return 0!==(t=Qe(t,!e)).length||e||(t="."),t.length>0&&r&&(t+="/"),e?"/"+t:t},isAbsolute:function(t){return Je(t),t.length>0&&47===t.charCodeAt(0)},join:function(){if(0===arguments.length)return".";for(var t,e=0;e0&&(void 0===t?t=r:t+="/"+r)}return void 0===t?".":$e.normalize(t)},relative:function(t,e){if(Je(t),Je(e),t===e)return"";if((t=$e.resolve(t))===(e=$e.resolve(e)))return"";for(var r=1;ra){if(47===e.charCodeAt(o+u))return e.slice(o+u+1);if(0===u)return e.slice(o+u)}else n>a&&(47===t.charCodeAt(r+u)?h=u:0===u&&(h=0));break}var f=t.charCodeAt(r+u);if(f!==e.charCodeAt(o+u))break;47===f&&(h=u)}var l="";for(u=r+h+1;u<=i;++u)u!==i&&47!==t.charCodeAt(u)||(0===l.length?l+="..":l+="/..");return l.length>0?l+e.slice(o+h):(o+=h,47===e.charCodeAt(o)&&++o,e.slice(o))},_makeLong:function(t){return t},dirname:function(t){if(Je(t),0===t.length)return".";for(var e=t.charCodeAt(0),r=47===e,i=-1,n=!0,o=t.length-1;o>=1;--o)if(47===(e=t.charCodeAt(o))){if(!n){i=o;break}}else n=!1;return-1===i?r?"/":".":r&&1===i?"//":t.slice(0,i)},basename:function(t,e){if(void 0!==e&&"string"!=typeof e)throw new TypeError('"ext" argument must be a string');Je(t);var r,i=0,n=-1,o=!0;if(void 0!==e&&e.length>0&&e.length<=t.length){if(e.length===t.length&&e===t)return"";var s=e.length-1,a=-1;for(r=t.length-1;r>=0;--r){var h=t.charCodeAt(r);if(47===h){if(!o){i=r+1;break}}else-1===a&&(o=!1,a=r+1),s>=0&&(h===e.charCodeAt(s)?-1==--s&&(n=r):(s=-1,n=a))}return i===n?n=a:-1===n&&(n=t.length),t.slice(i,n)}for(r=t.length-1;r>=0;--r)if(47===t.charCodeAt(r)){if(!o){i=r+1;break}}else-1===n&&(o=!1,n=r+1);return-1===n?"":t.slice(i,n)},extname:function(t){Je(t);for(var e=-1,r=0,i=-1,n=!0,o=0,s=t.length-1;s>=0;--s){var a=t.charCodeAt(s);if(47!==a)-1===i&&(n=!1,i=s+1),46===a?-1===e?e=s:1!==o&&(o=1):-1!==e&&(o=-1);else if(!n){r=s+1;break}}return-1===e||-1===i||0===o||1===o&&e===i-1&&e===r+1?"":t.slice(e,i)},format:function(t){if(null===t||"object"!=typeof t)throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof t);return function(t,e){var r=e.dir||e.root,i=e.base||(e.name||"")+(e.ext||"");return r?r===e.root?r+i:r+t+i:i}("/",t)},parse:function(t){Je(t);var e={root:"",dir:"",base:"",ext:"",name:""};if(0===t.length)return e;var r,i=t.charCodeAt(0),n=47===i;n?(e.root="/",r=1):r=0;for(var o=-1,s=0,a=-1,h=!0,u=t.length-1,f=0;u>=r;--u)if(47!==(i=t.charCodeAt(u)))-1===a&&(h=!1,a=u+1),46===i?-1===o?o=u:1!==f&&(f=1):-1!==o&&(f=-1);else if(!h){s=u+1;break}return-1===o||-1===a||0===f||1===f&&o===a-1&&o===s+1?-1!==a&&(e.base=e.name=0===s&&n?t.slice(1,a):t.slice(s,a)):(0===s&&n?(e.name=t.slice(1,o),e.base=t.slice(1,a)):(e.name=t.slice(s,o),e.base=t.slice(s,a)),e.ext=t.slice(o,a)),s>0?e.dir=t.slice(0,s-1):n&&(e.dir="/"),e},sep:"/",delimiter:":",win32:null,posix:null},$e.posix=$e,Ve=$e),Ve}var er,rr=!1;function ir(){return rr||(rr=!0,er={}),er}var nr,or,sr,ar,hr=!1;function ur(t,e){for(var r in t)e[r]=t[r]}function fr(t,e,r){return ar(t,e,r)}function lr(){return hr||(hr=!0,nr={},sr=Ye(),(ar=sr.Buffer).from&&ar.alloc&&ar.allocUnsafe&&ar.allocUnsafeSlow?nr=sr:(ur(sr,nr),or=fr,nr.Buffer=or),fr.prototype=Object.create(ar.prototype),ur(ar,fr),fr.from=function(t,e,r){if("number"==typeof t)throw new TypeError("Argument must not be a number");return ar(t,e,r)},fr.alloc=function(t,e,r){if("number"!=typeof t)throw new TypeError("Argument must be a number");var i=ar(t);return void 0!==e?"string"==typeof r?i.fill(e,r):i.fill(e):i.fill(0),i},fr.allocUnsafe=function(t){if("number"!=typeof t)throw new TypeError("Argument must be a number");return ar(t)},fr.allocUnsafeSlow=function(t){if("number"!=typeof t)throw new TypeError("Argument must be a number");return sr.SlowBuffer(t)}),nr}var dr,cr,pr,mr,gr,vr=!1;function br(){throw new Error("Secure random number generation is not supported by this browser.\nUse Chrome, Firefox or Internet Explorer 11")}function yr(t,e){if(t>pr)throw new RangeError("requested too many random bytes");var r=mr.allocUnsafe(t);if(t>0)if(t>cr)for(var i=0;i0&&s.length>n&&!s.warned){s.warned=!0;var h=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(e)+" listeners added. Use emitter.setMaxListeners() to increase limit");h.name="MaxListenersExceededWarning",h.emitter=t,h.type=e,h.count=s.length,a=h,console&&console.warn&&console.warn(a)}return t}function Lr(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function Or(t,e,r){var i={fired:!1,wrapFn:void 0,target:t,type:e,listener:r},n=Lr.bind(i);return n.listener=r,i.wrapFn=n,n}function jr(t,e,r){var i=t._events;if(void 0===i)return[];var n=i[e];return void 0===n?[]:"function"==typeof n?r?[n.listener||n]:[n]:r?function(t){for(var e=new Array(t.length),r=0;r0&&(o=e[0]),o instanceof Error)throw o;var s=new Error("Unhandled error."+(o?" ("+o.message+")":""));throw s.context=o,s}var a=n[t];if(void 0===a)return!1;if("function"==typeof a)kr(a,this,e);else{var h=a.length,u=Nr(a,h);for(r=0;r=0;o--)if(r[o]===e||r[o].listener===e){s=r[o].listener,n=o;break}if(n<0)return this;0===n?r.shift():function(t,e){for(;e+1=0;i--)this.removeListener(t,e[i]);return this},Ir.prototype.listeners=function(t){return jr(this,t,!0)},Ir.prototype.rawListeners=function(t){return jr(this,t,!1)},Ir.listenerCount=function(t,e){return"function"==typeof t.listenerCount?t.listenerCount(e):Ur.call(t,e)},Ir.prototype.listenerCount=Ur,Ir.prototype.eventNames=function(){return this._eventsCount>0?Br(this._events):[]}),Er}var qr,Fr=!1;function Zr(){return Fr||(Fr=!0,qr={},qr=zr().EventEmitter),qr}var Hr,Kr,Wr,Gr,Yr=!1;function Vr(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,i)}return r}function $r(t,e,r){return e in t?Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[e]=r,t}function Xr(t,e){for(var r=0;r0?this.tail.next=e:this.head=e,this.tail=e,++this.length}},{key:"unshift",value:function(t){var e={data:t,next:this.head};0===this.length&&(this.tail=e),this.head=e,++this.length}},{key:"shift",value:function(){if(0!==this.length){var t=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,t}}},{key:"clear",value:function(){this.head=this.tail=null,this.length=0}},{key:"join",value:function(t){if(0===this.length)return"";for(var e=this.head,r=""+e.data;e=e.next;)r+=t+e.data;return r}},{key:"concat",value:function(t){if(0===this.length)return Kr.alloc(0);for(var e,r,i,n=Kr.allocUnsafe(t>>>0),o=this.head,s=0;o;)e=o.data,r=n,i=s,Kr.prototype.copy.call(e,r,i),s+=o.data.length,o=o.next;return n}},{key:"consume",value:function(t,e){var r;return tn.length?n.length:t;if(o===n.length?i+=n:i+=n.slice(0,t),0==(t-=o)){o===n.length?(++r,e.next?this.head=e.next:this.head=this.tail=null):(this.head=e,e.data=n.slice(o));break}++r}return this.length-=r,i}},{key:"_getBuffer",value:function(t){var e=Kr.allocUnsafe(t),r=this.head,i=1;for(r.data.copy(e),t-=r.data.length;r=r.next;){var n=r.data,o=t>n.length?n.length:t;if(n.copy(e,e.length-t,0,o),0==(t-=o)){o===n.length?(++i,r.next?this.head=r.next:this.head=this.tail=null):(this.head=r,r.data=n.slice(o));break}++i}return this.length-=i,e}},{key:Gr,value:function(t,e){return Wr(this,function(t){for(var e=1;e2?"one of ".concat(e," ").concat(t.slice(0,r-1).join(", "),", or ")+t[r-1]:2===r?"one of ".concat(e," ").concat(t[0]," or ").concat(t[1]):"of ".concat(e," ").concat(t[0])}return"of ".concat(e," ").concat(String(t))}function pi(){return li||(li=!0,hi={},ui={},di("ERR_INVALID_OPT_VALUE",(function(t,e){return'The value "'+e+'" is invalid for option "'+t+'"'}),TypeError),di("ERR_INVALID_ARG_TYPE",(function(t,e,r){var i,n,o,s;if("string"==typeof e&&(n="not ",e.substr(!o||o<0?0:+o,n.length)===n)?(i="must not be",e=e.replace(/^not /,"")):i="must be",function(t,e,r){return(void 0===r||r>t.length)&&(r=t.length),t.substring(r-e.length,r)===e}(t," argument"))s="The ".concat(t," ").concat(i," ").concat(ci(e,"type"));else{var a=function(t,e,r){return"number"!=typeof r&&(r=0),!(r+e.length>t.length)&&-1!==t.indexOf(e,r)}(t,".")?"property":"argument";s='The "'.concat(t,'" ').concat(a," ").concat(i," ").concat(ci(e,"type"))}return s+". Received type ".concat(typeof r)}),TypeError),di("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF"),di("ERR_METHOD_NOT_IMPLEMENTED",(function(t){return"The "+t+" method is not implemented"})),di("ERR_STREAM_PREMATURE_CLOSE","Premature close"),di("ERR_STREAM_DESTROYED",(function(t){return"Cannot call "+t+" after a stream was destroyed"})),di("ERR_MULTIPLE_CALLBACK","Callback called multiple times"),di("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable"),di("ERR_STREAM_WRITE_AFTER_END","write after end"),di("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError),di("ERR_UNKNOWN_ENCODING",(function(t){return"Unknown encoding: "+t}),TypeError),di("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event"),fi=ui,hi.codes=fi),hi}var mi,gi,vi=!1;function bi(t,e,r,i){var n=function(t,e,r){return null!=t.highWaterMark?t.highWaterMark:e?t[r]:null}(e,i,r);if(null!=n){if(!isFinite(n)||Math.floor(n)!==n||n<0)throw new gi(i?r:"highWaterMark",n);return Math.floor(n)}return t.objectMode?16:16384}function yi(){return vi||(vi=!0,mi={},gi=pi().codes.ERR_INVALID_OPT_VALUE,mi={getHighWaterMark:bi}),mi}var wi,Mi=!1;function _i(t,e){if(Ai("noDeprecation"))return t;var r=!1;return function(){if(!r){if(Ai("throwDeprecation"))throw new Error(e);Ai("traceDeprecation")?console.trace(e):console.warn(e),r=!0}return t.apply(this,arguments)}}function Ai(e){try{if(!t.localStorage)return!1}catch(t){return!1}var r=t.localStorage[e];return null!=r&&"true"===String(r).toLowerCase()}function Ei(){return Mi||(Mi=!0,wi={},wi=_i),wi}var Si,ki,Bi,Ri,Ti,xi,Ii,Ci,Pi,Di,Li,Oi,ji,Ui,Ni,zi,qi,Fi,Zi=!1;function Hi(t){var e=this;this.next=null,this.entry=null,this.finish=function(){!function(t,e,r){var i=t.entry;t.entry=null;for(;i;){var n=i.callback;e.pendingcb--,n(r),i=i.next}e.corkedRequestsFree.next=t}(e,t)}}function Ki(){}function Wi(t,e,r){ki=ki||pn(),t=t||{},"boolean"!=typeof r&&(r=e instanceof ki),this.objectMode=!!t.objectMode,r&&(this.objectMode=this.objectMode||!!t.writableObjectMode),this.highWaterMark=Ii(this,t,"writableHighWaterMark",r),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var i=!1===t.decodeStrings;this.decodeStrings=!i,this.defaultEncoding=t.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(t){!function(t,e){var r=t._writableState,i=r.sync,n=r.writecb;if("function"!=typeof n)throw new Li;if(function(t){t.writing=!1,t.writecb=null,t.length-=t.writelen,t.writelen=0}(r),e)!function(t,e,r,i,n){--e.pendingcb,r?(jt().nextTick(n,i),jt().nextTick(tn,t,e),t._writableState.errorEmitted=!0,qi(t,i)):(n(i),t._writableState.errorEmitted=!0,qi(t,i),tn(t,e))}(t,r,i,e,n);else{var o=Ji(r)||t.destroyed;o||r.corked||r.bufferProcessing||!r.bufferedRequest||Xi(t,r),i?jt().nextTick($i,t,r,o,n):$i(t,r,o,n)}}(e,t)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=!1!==t.emitClose,this.autoDestroy=!!t.autoDestroy,this.bufferedRequestCount=0,this.corkedRequestsFree=new Hi(this)}function Gi(t){var e=this instanceof(ki=ki||pn());if(!e&&!Fi.call(Gi,this))return new Gi(t);this._writableState=new Wi(t,this,e),this.writable=!0,t&&("function"==typeof t.write&&(this._write=t.write),"function"==typeof t.writev&&(this._writev=t.writev),"function"==typeof t.destroy&&(this._destroy=t.destroy),"function"==typeof t.final&&(this._final=t.final)),Ri.call(this)}function Yi(t,e,r,i,n,o){if(!r){var s=function(t,e,r){return t.objectMode||!1===t.decodeStrings||"string"!=typeof e||(e=Ti.from(e,r)),e}(e,i,n);i!==s&&(r=!0,n="buffer",i=s)}var a=e.objectMode?1:i.length;e.length+=a;var h=e.length-1))throw new zi(t);return this._writableState.defaultEncoding=t,this},Object.defineProperty(Gi.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(Gi.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),Gi.prototype._write=function(t,e,r){r(new Di("_write()"))},Gi.prototype._writev=null,Gi.prototype.end=function(t,e,r){var i=this._writableState;return"function"==typeof t?(r=t,t=null,e=null):"function"==typeof e&&(r=e,e=null),null!=t&&this.write(t,e),i.corked&&(i.corked=1,this.uncork()),i.ending||function(t,e,r){e.ending=!0,tn(t,e),r&&(e.finished?jt().nextTick(r):t.once("finish",r)),e.ended=!0,t.writable=!1}(this,i,r),this},Object.defineProperty(Gi.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(Gi.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(t){this._writableState&&(this._writableState.destroyed=t)}}),Gi.prototype.destroy=ai().destroy,Gi.prototype._undestroy=ai().undestroy,Gi.prototype._destroy=function(t,e){e(t)}}function rn(){return Zi||(Zi=!0,en()),Si}var nn,on,sn,an,hn,un,fn=!1;function ln(t){if(!(this instanceof ln))return new ln(t);sn.call(this,t),rn().call(this,t),this.allowHalfOpen=!0,t&&(!1===t.readable&&(this.readable=!1),!1===t.writable&&(this.writable=!1),!1===t.allowHalfOpen&&(this.allowHalfOpen=!1,this.once("end",dn)))}function dn(){this._writableState.ended||jt().nextTick(cn,this)}function cn(t){t.end()}function pn(){return fn||(fn=!0,function(){for(nn={},jt(),on=Object.keys||function(t){var e=[];for(var r in t)e.push(r);return e},nn=ln,sn=Wo(),rn(),Ar()(ln,sn),an=on(rn().prototype),hn=0;hn>5==6?2:t>>4==14?3:t>>3==30?4:t>>6==2?-1:-2}function _n(t){var e=this.lastTotal-this.lastNeed,r=function(t,e,r){if(128!=(192&e[0]))return t.lastNeed=0,"�";if(t.lastNeed>1&&e.length>1){if(128!=(192&e[1]))return t.lastNeed=1,"�";if(t.lastNeed>2&&e.length>2&&128!=(192&e[2]))return t.lastNeed=2,"�"}}(this,t);return void 0!==r?r:this.lastNeed<=t.length?(t.copy(this.lastChar,e,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(t.copy(this.lastChar,e,0,t.length),void(this.lastNeed-=t.length))}function An(t,e){var r=function(t,e,r){var i=e.length-1;if(i=0?(n>0&&(t.lastNeed=n-1),n):--i=0?(n>0&&(t.lastNeed=n-2),n):--i=0?(n>0&&(2===n?n=0:t.lastNeed=n-3),n):0}(this,t,e);if(!this.lastNeed)return t.toString("utf8",e);this.lastTotal=r;var i=t.length-(r-this.lastNeed);return t.copy(this.lastChar,0,i),t.toString("utf8",e,i)}function En(t){var e=t&&t.length?this.write(t):"";return this.lastNeed?e+"�":e}function Sn(t,e){if((t.length-e)%2==0){var r=t.toString("utf16le",e);if(r){var i=r.charCodeAt(r.length-1);if(i>=55296&&i<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=t[t.length-2],this.lastChar[1]=t[t.length-1],r.slice(0,-1)}return r}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=t[t.length-1],t.toString("utf16le",e,t.length-1)}function kn(t){var e=t&&t.length?this.write(t):"";if(this.lastNeed){var r=this.lastTotal-this.lastNeed;return e+this.lastChar.toString("utf16le",0,r)}return e}function Bn(t,e){var r=(t.length-e)%3;return 0===r?t.toString("base64",e):(this.lastNeed=3-r,this.lastTotal=3,1===r?this.lastChar[0]=t[t.length-1]:(this.lastChar[0]=t[t.length-2],this.lastChar[1]=t[t.length-1]),t.toString("base64",e,t.length-r))}function Rn(t){var e=t&&t.length?this.write(t):"";return this.lastNeed?e+this.lastChar.toString("base64",0,3-this.lastNeed):e}function Tn(t){return t.toString(this.encoding)}function xn(t){return t&&t.length?this.write(t):""}function In(){return yn||(yn=!0,mn={},gn=lr().Buffer,vn=gn.isEncoding||function(t){switch((t=""+t)&&t.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}},bn=wn,mn.StringDecoder=bn,wn.prototype.write=function(t){if(0===t.length)return"";var e,r;if(this.lastNeed){if(void 0===(e=this.fillLast(t)))return"";r=this.lastNeed,this.lastNeed=0}else r=0;return r0)if("string"==typeof e||s.objectMode||Object.getPrototypeOf(e)===uo.prototype||(e=function(t){return uo.from(t)}(e)),i)s.endEmitted?Eo(t,new wo):Io(t,s,e,!0);else if(s.ended)Eo(t,new bo);else{if(s.destroyed)return!1;s.reading=!1,s.decoder&&!r?(e=s.decoder.write(e),s.objectMode||0!==e.length?Io(t,s,e,!1):Lo(t,s)):Io(t,s,e,!1)}else i||(s.reading=!1,Lo(t,s));return!s.ended&&(s.lengthe.highWaterMark&&(e.highWaterMark=function(t){return t>=ko?t=ko:(t--,t|=t>>>1,t|=t>>>2,t|=t>>>4,t|=t>>>8,t|=t>>>16,t++),t}(t)),t<=e.length?t:e.ended?e.length:(e.needReadable=!0,0))}function Po(t){var e=t._readableState;co("emitReadable",e.needReadable,e.emittedReadable),e.needReadable=!1,e.emittedReadable||(co("emitReadable",e.flowing),e.emittedReadable=!0,jt().nextTick(Do,t))}function Do(t){var e=t._readableState;co("emitReadable_",e.destroyed,e.length,e.ended),e.destroyed||!e.length&&!e.ended||(t.emit("readable"),e.emittedReadable=!1),e.needReadable=!e.flowing&&!e.ended&&e.length<=e.highWaterMark,zo(t)}function Lo(t,e){e.readingMore||(e.readingMore=!0,jt().nextTick(Oo,t,e))}function Oo(t,e){for(;!e.reading&&!e.ended&&(e.length0,e.resumeScheduled&&!e.paused?e.flowing=!0:t.listenerCount("data")>0&&t.resume()}function Uo(t){co("readable nexttick read 0"),t.read(0)}function No(t,e){co("resume",e.reading),e.reading||t.read(0),e.resumeScheduled=!1,t.emit("resume"),zo(t),e.flowing&&!e.reading&&t.read(0)}function zo(t){var e=t._readableState;for(co("flow",e.flowing);e.flowing&&null!==t.read(););}function qo(t,e){return 0===e.length?null:(e.objectMode?r=e.buffer.shift():!t||t>=e.length?(r=e.decoder?e.buffer.join(""):1===e.buffer.length?e.buffer.first():e.buffer.concat(e.length),e.buffer.clear()):r=e.buffer.consume(t,e.decoder),r);var r}function Fo(t){var e=t._readableState;co("endReadable",e.endEmitted),e.endEmitted||(e.ended=!0,jt().nextTick(Zo,e,t))}function Zo(t,e){if(co("endReadableNT",t.endEmitted,t.length),!t.endEmitted&&0===t.length&&(t.endEmitted=!0,e.readable=!1,e.emit("end"),t.autoDestroy)){var r=e._writableState;(!r||r.autoDestroy&&r.finished)&&e.destroy()}}function Ho(t,e){for(var r=0,i=t.length;r=e.highWaterMark:e.length>0)||e.ended))return co("read: emitReadable",e.length,e.ended),0===e.length&&e.ended?Fo(this):Po(this),null;if(0===(t=Co(t,e))&&e.ended)return 0===e.length&&Fo(this),null;var i,n=e.needReadable;return co("need readable",n),(0===e.length||e.length-t0?qo(t,e):null)?(e.needReadable=e.length<=e.highWaterMark,t=0):(e.length-=t,e.awaitDrain=0),0===e.length&&(e.ended||(e.needReadable=!0),r!==t&&e.ended&&Fo(this)),null!==i&&this.emit("data",i),i},To.prototype._read=function(t){Eo(this,new yo("_read()"))},To.prototype.pipe=function(t,e){var r=this,i=this._readableState;switch(i.pipesCount){case 0:i.pipes=t;break;case 1:i.pipes=[i.pipes,t];break;default:i.pipes.push(t)}i.pipesCount+=1,co("pipe count=%d opts=%j",i.pipesCount,e);var n=(!e||!1!==e.end)&&t!==jt().stdout&&t!==jt().stderr?s:c;function o(e,n){co("onunpipe"),e===r&&n&&!1===n.hasUnpiped&&(n.hasUnpiped=!0,co("cleanup"),t.removeListener("close",l),t.removeListener("finish",d),t.removeListener("drain",a),t.removeListener("error",f),t.removeListener("unpipe",o),r.removeListener("end",s),r.removeListener("end",c),r.removeListener("data",u),h=!0,!i.awaitDrain||t._writableState&&!t._writableState.needDrain||a())}function s(){co("onend"),t.end()}i.endEmitted?jt().nextTick(n):r.once("end",n),t.on("unpipe",o);var a=function(t){return function(){var e=t._readableState;co("pipeOnDrain",e.awaitDrain),e.awaitDrain&&e.awaitDrain--,0===e.awaitDrain&&ao(t,"data")&&(e.flowing=!0,zo(t))}}(r);t.on("drain",a);var h=!1;function u(e){co("ondata");var n=t.write(e);co("dest.write",n),!1===n&&((1===i.pipesCount&&i.pipes===t||i.pipesCount>1&&-1!==Ho(i.pipes,t))&&!h&&(co("false write response, pause",i.awaitDrain),i.awaitDrain++),r.pause())}function f(e){co("onerror",e),c(),t.removeListener("error",f),0===ao(t,"error")&&Eo(t,e)}function l(){t.removeListener("finish",d),c()}function d(){co("onfinish"),t.removeListener("close",l),c()}function c(){co("unpipe"),r.unpipe(t)}return r.on("data",u),function(t,e,r){if("function"==typeof t.prependListener)return t.prependListener(e,r);t._events&&t._events[e]?Array.isArray(t._events[e])?t._events[e].unshift(r):t._events[e]=[r,t._events[e]]:t.on(e,r)}(t,"error",f),t.once("close",l),t.once("finish",d),t.emit("pipe",r),i.flowing||(co("pipe resume"),r.resume()),t},To.prototype.unpipe=function(t){var e=this._readableState,r={hasUnpiped:!1};if(0===e.pipesCount)return this;if(1===e.pipesCount)return t&&t!==e.pipes||(t||(t=e.pipes),e.pipes=null,e.pipesCount=0,e.flowing=!1,t&&t.emit("unpipe",this,r)),this;if(!t){var i=e.pipes,n=e.pipesCount;e.pipes=null,e.pipesCount=0,e.flowing=!1;for(var o=0;o0,!1!==i.flowing&&this.resume()):"readable"===t&&(i.endEmitted||i.readableListening||(i.readableListening=i.needReadable=!0,i.flowing=!1,i.emittedReadable=!1,co("on readable",i.length,i.reading),i.length?Po(this):i.reading||jt().nextTick(Uo,this))),r},To.prototype.addListener=To.prototype.on,To.prototype.removeListener=function(t,e){var r=ho.prototype.removeListener.call(this,t,e);return"readable"===t&&jt().nextTick(jo,this),r},To.prototype.removeAllListeners=function(t){var e=ho.prototype.removeAllListeners.apply(this,arguments);return"readable"!==t&&void 0!==t||jt().nextTick(jo,this),e},To.prototype.resume=function(){var t=this._readableState;return t.flowing||(co("resume"),t.flowing=!t.readableListening,function(t,e){e.resumeScheduled||(e.resumeScheduled=!0,jt().nextTick(No,t,e))}(this,t)),t.paused=!1,this},To.prototype.pause=function(){return co("call pause flowing=%j",this._readableState.flowing),!1!==this._readableState.flowing&&(co("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState.paused=!0,this},To.prototype.wrap=function(t){var e=this,r=this._readableState,i=!1;for(var n in t.on("end",(function(){if(co("wrapped end"),r.decoder&&!r.ended){var t=r.decoder.end();t&&t.length&&e.push(t)}e.push(null)})),t.on("data",(function(n){(co("wrapped data"),r.decoder&&(n=r.decoder.write(n)),r.objectMode&&null==n)||(r.objectMode||n&&n.length)&&(e.push(n)||(i=!0,t.pause()))})),t)void 0===this[n]&&"function"==typeof t[n]&&(this[n]=function(e){return function(){return t[e].apply(t,arguments)}}(n));for(var o=0;o0,(function(t){i||(i=t),t&&o.forEach(ys),s||(o.forEach(ys),n(i))}))}));return e.reduce(ws)}function As(){return gs||(gs=!0,ls={},cs=pi().codes,ps=cs.ERR_MISSING_ARGS,ms=cs.ERR_STREAM_DESTROYED,ls=_s),ls}var Es,Ss,ks,Bs,Rs,Ts,xs,Is,Cs,Ps,Ds=!1;function Ls(){return Ds||(Ds=!0,Ss={},Es=Ss=Wo(),ks=Es,Ss.Stream=ks,Bs=Es,Ss.Readable=Bs,Rs=rn(),Ss.Writable=Rs,Ts=pn(),Ss.Duplex=Ts,xs=os(),Ss.Transform=xs,Is=fs(),Ss.PassThrough=Is,Cs=jn(),Ss.finished=Cs,Ps=As(),Ss.pipeline=Ps),Ss}var Os,js,Us,Ns=!1;function zs(t){Us.call(this),this._block=js.allocUnsafe(t),this._blockSize=t,this._blockOffset=0,this._length=[0,0,0,0],this._finalized=!1}function qs(){Os={},js=lr().Buffer,Us=Ls().Transform,Ar()(zs,Us),zs.prototype._transform=function(t,e,r){var i=null;try{this.update(t,e)}catch(t){i=t}r(i)},zs.prototype._flush=function(t){var e=null;try{this.push(this.digest())}catch(t){e=t}t(e)},zs.prototype.update=function(t,e){if(function(t,e){if(!js.isBuffer(t)&&"string"!=typeof t)throw new TypeError(e+" must be a string or a buffer")}(t,"Data"),this._finalized)throw new Error("Digest already called");js.isBuffer(t)||(t=js.from(t,e));for(var r=this._block,i=0;this._blockOffset+t.length-i>=this._blockSize;){for(var n=this._blockOffset;n0;++o)this._length[o]+=s,(s=this._length[o]/4294967296|0)>0&&(this._length[o]-=4294967296*s);return this},zs.prototype._update=function(){throw new Error("_update is not implemented")},zs.prototype.digest=function(t){if(this._finalized)throw new Error("Digest already called");this._finalized=!0;var e=this._digest();void 0!==t&&(e=e.toString(t)),this._block.fill(0),this._blockOffset=0;for(var r=0;r<4;++r)this._length[r]=0;return e},zs.prototype._digest=function(){throw new Error("_digest is not implemented")},Os=zs}function Fs(){return Ns||(Ns=!0,qs()),Os}var Zs,Hs,Ks,Ws,Gs,Ys=!1;function Vs(){Ks.call(this,64),this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878}function $s(t,e){return t<>>32-e}function Xs(t,e,r,i,n,o,s){return $s(t+(e&r|~e&i)+n+o|0,s)+e|0}function Js(t,e,r,i,n,o,s){return $s(t+(e&i|r&~i)+n+o|0,s)+e|0}function Qs(t,e,r,i,n,o,s){return $s(t+(e^r^i)+n+o|0,s)+e|0}function ta(t,e,r,i,n,o,s){return $s(t+(r^(e|~i))+n+o|0,s)+e|0}function ea(){return Ys||(Ys=!0,Zs={},Hs=Ar(),Ks=Fs(),Ws=lr().Buffer,Gs=new Array(16),Hs(Vs,Ks),Vs.prototype._update=function(){for(var t=Gs,e=0;e<16;++e)t[e]=this._block.readInt32LE(4*e);var r=this._a,i=this._b,n=this._c,o=this._d;r=Xs(r,i,n,o,t[0],3614090360,7),o=Xs(o,r,i,n,t[1],3905402710,12),n=Xs(n,o,r,i,t[2],606105819,17),i=Xs(i,n,o,r,t[3],3250441966,22),r=Xs(r,i,n,o,t[4],4118548399,7),o=Xs(o,r,i,n,t[5],1200080426,12),n=Xs(n,o,r,i,t[6],2821735955,17),i=Xs(i,n,o,r,t[7],4249261313,22),r=Xs(r,i,n,o,t[8],1770035416,7),o=Xs(o,r,i,n,t[9],2336552879,12),n=Xs(n,o,r,i,t[10],4294925233,17),i=Xs(i,n,o,r,t[11],2304563134,22),r=Xs(r,i,n,o,t[12],1804603682,7),o=Xs(o,r,i,n,t[13],4254626195,12),n=Xs(n,o,r,i,t[14],2792965006,17),r=Js(r,i=Xs(i,n,o,r,t[15],1236535329,22),n,o,t[1],4129170786,5),o=Js(o,r,i,n,t[6],3225465664,9),n=Js(n,o,r,i,t[11],643717713,14),i=Js(i,n,o,r,t[0],3921069994,20),r=Js(r,i,n,o,t[5],3593408605,5),o=Js(o,r,i,n,t[10],38016083,9),n=Js(n,o,r,i,t[15],3634488961,14),i=Js(i,n,o,r,t[4],3889429448,20),r=Js(r,i,n,o,t[9],568446438,5),o=Js(o,r,i,n,t[14],3275163606,9),n=Js(n,o,r,i,t[3],4107603335,14),i=Js(i,n,o,r,t[8],1163531501,20),r=Js(r,i,n,o,t[13],2850285829,5),o=Js(o,r,i,n,t[2],4243563512,9),n=Js(n,o,r,i,t[7],1735328473,14),r=Qs(r,i=Js(i,n,o,r,t[12],2368359562,20),n,o,t[5],4294588738,4),o=Qs(o,r,i,n,t[8],2272392833,11),n=Qs(n,o,r,i,t[11],1839030562,16),i=Qs(i,n,o,r,t[14],4259657740,23),r=Qs(r,i,n,o,t[1],2763975236,4),o=Qs(o,r,i,n,t[4],1272893353,11),n=Qs(n,o,r,i,t[7],4139469664,16),i=Qs(i,n,o,r,t[10],3200236656,23),r=Qs(r,i,n,o,t[13],681279174,4),o=Qs(o,r,i,n,t[0],3936430074,11),n=Qs(n,o,r,i,t[3],3572445317,16),i=Qs(i,n,o,r,t[6],76029189,23),r=Qs(r,i,n,o,t[9],3654602809,4),o=Qs(o,r,i,n,t[12],3873151461,11),n=Qs(n,o,r,i,t[15],530742520,16),r=ta(r,i=Qs(i,n,o,r,t[2],3299628645,23),n,o,t[0],4096336452,6),o=ta(o,r,i,n,t[7],1126891415,10),n=ta(n,o,r,i,t[14],2878612391,15),i=ta(i,n,o,r,t[5],4237533241,21),r=ta(r,i,n,o,t[12],1700485571,6),o=ta(o,r,i,n,t[3],2399980690,10),n=ta(n,o,r,i,t[10],4293915773,15),i=ta(i,n,o,r,t[1],2240044497,21),r=ta(r,i,n,o,t[8],1873313359,6),o=ta(o,r,i,n,t[15],4264355552,10),n=ta(n,o,r,i,t[6],2734768916,15),i=ta(i,n,o,r,t[13],1309151649,21),r=ta(r,i,n,o,t[4],4149444226,6),o=ta(o,r,i,n,t[11],3174756917,10),n=ta(n,o,r,i,t[2],718787259,15),i=ta(i,n,o,r,t[9],3951481745,21),this._a=this._a+r|0,this._b=this._b+i|0,this._c=this._c+n|0,this._d=this._d+o|0},Vs.prototype._digest=function(){this._block[this._blockOffset++]=128,this._blockOffset>56&&(this._block.fill(0,this._blockOffset,64),this._update(),this._blockOffset=0),this._block.fill(0,this._blockOffset,56),this._block.writeUInt32LE(this._length[0],56),this._block.writeUInt32LE(this._length[1],60),this._update();var t=Ws.allocUnsafe(16);return t.writeInt32LE(this._a,0),t.writeInt32LE(this._b,4),t.writeInt32LE(this._c,8),t.writeInt32LE(this._d,12),t},Zs=Vs),Zs}var ra,ia,na,oa,sa,aa,ha,ua,fa,la,da,ca=!1;function pa(){oa.call(this,64),this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520}function ma(t,e){return t<>>32-e}function ga(t,e,r,i,n,o,s,a){return ma(t+(e^r^i)+o+s|0,a)+n|0}function va(t,e,r,i,n,o,s,a){return ma(t+(e&r|~e&i)+o+s|0,a)+n|0}function ba(t,e,r,i,n,o,s,a){return ma(t+((e|~r)^i)+o+s|0,a)+n|0}function ya(t,e,r,i,n,o,s,a){return ma(t+(e&i|r&~i)+o+s|0,a)+n|0}function wa(t,e,r,i,n,o,s,a){return ma(t+(e^(r|~i))+o+s|0,a)+n|0}function Ma(){return ca||(ca=!0,ra={},ia=Ye().Buffer,na=Ar(),oa=Fs(),sa=new Array(16),aa=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13],ha=[5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11],ua=[11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6],fa=[8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11],la=[0,1518500249,1859775393,2400959708,2840853838],da=[1352829926,1548603684,1836072691,2053994217,0],na(pa,oa),pa.prototype._update=function(){for(var t=sa,e=0;e<16;++e)t[e]=this._block.readInt32LE(4*e);for(var r=0|this._a,i=0|this._b,n=0|this._c,o=0|this._d,s=0|this._e,a=0|this._a,h=0|this._b,u=0|this._c,f=0|this._d,l=0|this._e,d=0;d<80;d+=1){var c,p;d<16?(c=ga(r,i,n,o,s,t[aa[d]],la[0],ua[d]),p=wa(a,h,u,f,l,t[ha[d]],da[0],fa[d])):d<32?(c=va(r,i,n,o,s,t[aa[d]],la[1],ua[d]),p=ya(a,h,u,f,l,t[ha[d]],da[1],fa[d])):d<48?(c=ba(r,i,n,o,s,t[aa[d]],la[2],ua[d]),p=ba(a,h,u,f,l,t[ha[d]],da[2],fa[d])):d<64?(c=ya(r,i,n,o,s,t[aa[d]],la[3],ua[d]),p=va(a,h,u,f,l,t[ha[d]],da[3],fa[d])):(c=wa(r,i,n,o,s,t[aa[d]],la[4],ua[d]),p=ga(a,h,u,f,l,t[ha[d]],da[4],fa[d])),r=s,s=o,o=ma(n,10),n=i,i=c,a=l,l=f,f=ma(u,10),u=h,h=p}var m=this._b+n+f|0;this._b=this._c+o+l|0,this._c=this._d+s+a|0,this._d=this._e+r+h|0,this._e=this._a+i+u|0,this._a=m},pa.prototype._digest=function(){this._block[this._blockOffset++]=128,this._blockOffset>56&&(this._block.fill(0,this._blockOffset,64),this._update(),this._blockOffset=0),this._block.fill(0,this._blockOffset,56),this._block.writeUInt32LE(this._length[0],56),this._block.writeUInt32LE(this._length[1],60),this._update();var t=ia.alloc?ia.alloc(20):new ia(20);return t.writeInt32LE(this._a,0),t.writeInt32LE(this._b,4),t.writeInt32LE(this._c,8),t.writeInt32LE(this._d,12),t.writeInt32LE(this._e,16),t},ra=pa),ra}var _a,Aa,Ea=!1;function Sa(t,e){this._block=Aa.alloc(t),this._finalSize=e,this._blockSize=t,this._len=0}function ka(){return Ea||(Ea=!0,_a={},Aa=lr().Buffer,Sa.prototype.update=function(t,e){"string"==typeof t&&(e=e||"utf8",t=Aa.from(t,e));for(var r=this._block,i=this._blockSize,n=t.length,o=this._len,s=0;s=this._finalSize&&(this._update(this._block),this._block.fill(0));var r=8*this._len;if(r<=4294967295)this._block.writeUInt32BE(r,this._blockSize-4);else{var i=(4294967295&r)>>>0,n=(r-i)/4294967296;this._block.writeUInt32BE(n,this._blockSize-8),this._block.writeUInt32BE(i,this._blockSize-4)}this._update(this._block);var o=this._hash();return t?o.toString(t):o},Sa.prototype._update=function(){throw new Error("_update must be implemented by subclass")},_a=Sa),_a}var Ba,Ra,Ta,xa,Ia,Ca,Pa=!1;function Da(){this.init(),this._w=Ca,Ta.call(this,64,56)}function La(t){return t<<30|t>>>2}function Oa(t,e,r,i){return 0===t?e&r|~e&i:2===t?e&r|e&i|r&i:e^r^i}function ja(){return Pa||(Pa=!0,Ba={},Ra=Ar(),Ta=ka(),xa=lr().Buffer,Ia=[1518500249,1859775393,-1894007588,-899497514],Ca=new Array(80),Ra(Da,Ta),Da.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},Da.prototype._update=function(t){for(var e,r=this._w,i=0|this._a,n=0|this._b,o=0|this._c,s=0|this._d,a=0|this._e,h=0;h<16;++h)r[h]=t.readInt32BE(4*h);for(;h<80;++h)r[h]=r[h-3]^r[h-8]^r[h-14]^r[h-16];for(var u=0;u<80;++u){var f=~~(u/20),l=0|((e=i)<<5|e>>>27)+Oa(f,n,o,s)+a+r[u]+Ia[f];a=s,s=o,o=La(n),n=i,i=l}this._a=i+this._a|0,this._b=n+this._b|0,this._c=o+this._c|0,this._d=s+this._d|0,this._e=a+this._e|0},Da.prototype._hash=function(){var t=xa.allocUnsafe(20);return t.writeInt32BE(0|this._a,0),t.writeInt32BE(0|this._b,4),t.writeInt32BE(0|this._c,8),t.writeInt32BE(0|this._d,12),t.writeInt32BE(0|this._e,16),t},Ba=Da),Ba}var Ua,Na,za,qa,Fa,Za,Ha=!1;function Ka(){this.init(),this._w=Za,za.call(this,64,56)}function Wa(t){return t<<5|t>>>27}function Ga(t){return t<<30|t>>>2}function Ya(t,e,r,i){return 0===t?e&r|~e&i:2===t?e&r|e&i|r&i:e^r^i}function Va(){return Ha||(Ha=!0,Ua={},Na=Ar(),za=ka(),qa=lr().Buffer,Fa=[1518500249,1859775393,-1894007588,-899497514],Za=new Array(80),Na(Ka,za),Ka.prototype.init=function(){return this._a=1732584193,this._b=4023233417,this._c=2562383102,this._d=271733878,this._e=3285377520,this},Ka.prototype._update=function(t){for(var e,r=this._w,i=0|this._a,n=0|this._b,o=0|this._c,s=0|this._d,a=0|this._e,h=0;h<16;++h)r[h]=t.readInt32BE(4*h);for(;h<80;++h)r[h]=(e=r[h-3]^r[h-8]^r[h-14]^r[h-16])<<1|e>>>31;for(var u=0;u<80;++u){var f=~~(u/20),l=Wa(i)+Ya(f,n,o,s)+a+r[u]+Fa[f]|0;a=s,s=o,o=Ga(n),n=i,i=l}this._a=i+this._a|0,this._b=n+this._b|0,this._c=o+this._c|0,this._d=s+this._d|0,this._e=a+this._e|0},Ka.prototype._hash=function(){var t=qa.allocUnsafe(20);return t.writeInt32BE(0|this._a,0),t.writeInt32BE(0|this._b,4),t.writeInt32BE(0|this._c,8),t.writeInt32BE(0|this._d,12),t.writeInt32BE(0|this._e,16),t},Ua=Ka),Ua}var $a,Xa,Ja,Qa,th,eh,rh=!1;function ih(){this.init(),this._w=eh,Ja.call(this,64,56)}function nh(t,e,r){return r^t&(e^r)}function oh(t,e,r){return t&e|r&(t|e)}function sh(t){return(t>>>2|t<<30)^(t>>>13|t<<19)^(t>>>22|t<<10)}function ah(t){return(t>>>6|t<<26)^(t>>>11|t<<21)^(t>>>25|t<<7)}function hh(t){return(t>>>7|t<<25)^(t>>>18|t<<14)^t>>>3}function uh(){return rh||(rh=!0,$a={},Xa=Ar(),Ja=ka(),Qa=lr().Buffer,th=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],eh=new Array(64),Xa(ih,Ja),ih.prototype.init=function(){return this._a=1779033703,this._b=3144134277,this._c=1013904242,this._d=2773480762,this._e=1359893119,this._f=2600822924,this._g=528734635,this._h=1541459225,this},ih.prototype._update=function(t){for(var e,r=this._w,i=0|this._a,n=0|this._b,o=0|this._c,s=0|this._d,a=0|this._e,h=0|this._f,u=0|this._g,f=0|this._h,l=0;l<16;++l)r[l]=t.readInt32BE(4*l);for(;l<64;++l)r[l]=0|(((e=r[l-2])>>>17|e<<15)^(e>>>19|e<<13)^e>>>10)+r[l-7]+hh(r[l-15])+r[l-16];for(var d=0;d<64;++d){var c=f+ah(a)+nh(a,h,u)+th[d]+r[d]|0,p=sh(i)+oh(i,n,o)|0;f=u,u=h,h=a,a=s+c|0,s=o,o=n,n=i,i=c+p|0}this._a=i+this._a|0,this._b=n+this._b|0,this._c=o+this._c|0,this._d=s+this._d|0,this._e=a+this._e|0,this._f=h+this._f|0,this._g=u+this._g|0,this._h=f+this._h|0},ih.prototype._hash=function(){var t=Qa.allocUnsafe(32);return t.writeInt32BE(this._a,0),t.writeInt32BE(this._b,4),t.writeInt32BE(this._c,8),t.writeInt32BE(this._d,12),t.writeInt32BE(this._e,16),t.writeInt32BE(this._f,20),t.writeInt32BE(this._g,24),t.writeInt32BE(this._h,28),t},$a=ih),$a}var fh,lh,dh,ch,ph,mh=!1;function gh(){this.init(),this._w=ph,ka().call(this,64,56)}function vh(){return mh||(mh=!0,fh={},lh=Ar(),dh=uh(),ka(),ch=lr().Buffer,ph=new Array(64),lh(gh,dh),gh.prototype.init=function(){return this._a=3238371032,this._b=914150663,this._c=812702999,this._d=4144912697,this._e=4290775857,this._f=1750603025,this._g=1694076839,this._h=3204075428,this},gh.prototype._hash=function(){var t=ch.allocUnsafe(28);return t.writeInt32BE(this._a,0),t.writeInt32BE(this._b,4),t.writeInt32BE(this._c,8),t.writeInt32BE(this._d,12),t.writeInt32BE(this._e,16),t.writeInt32BE(this._f,20),t.writeInt32BE(this._g,24),t},fh=gh),fh}var bh,yh,wh,Mh,_h,Ah,Eh=!1;function Sh(){this.init(),this._w=Ah,wh.call(this,128,112)}function kh(t,e,r){return r^t&(e^r)}function Bh(t,e,r){return t&e|r&(t|e)}function Rh(t,e){return(t>>>28|e<<4)^(e>>>2|t<<30)^(e>>>7|t<<25)}function Th(t,e){return(t>>>14|e<<18)^(t>>>18|e<<14)^(e>>>9|t<<23)}function xh(t,e){return(t>>>1|e<<31)^(t>>>8|e<<24)^t>>>7}function Ih(t,e){return(t>>>1|e<<31)^(t>>>8|e<<24)^(t>>>7|e<<25)}function Ch(t,e){return(t>>>19|e<<13)^(e>>>29|t<<3)^t>>>6}function Ph(t,e){return(t>>>19|e<<13)^(e>>>29|t<<3)^(t>>>6|e<<26)}function Dh(t,e){return t>>>0>>0?1:0}function Lh(){return Eh||(Eh=!0,bh={},yh=Ar(),wh=ka(),Mh=lr().Buffer,_h=[1116352408,3609767458,1899447441,602891725,3049323471,3964484399,3921009573,2173295548,961987163,4081628472,1508970993,3053834265,2453635748,2937671579,2870763221,3664609560,3624381080,2734883394,310598401,1164996542,607225278,1323610764,1426881987,3590304994,1925078388,4068182383,2162078206,991336113,2614888103,633803317,3248222580,3479774868,3835390401,2666613458,4022224774,944711139,264347078,2341262773,604807628,2007800933,770255983,1495990901,1249150122,1856431235,1555081692,3175218132,1996064986,2198950837,2554220882,3999719339,2821834349,766784016,2952996808,2566594879,3210313671,3203337956,3336571891,1034457026,3584528711,2466948901,113926993,3758326383,338241895,168717936,666307205,1188179964,773529912,1546045734,1294757372,1522805485,1396182291,2643833823,1695183700,2343527390,1986661051,1014477480,2177026350,1206759142,2456956037,344077627,2730485921,1290863460,2820302411,3158454273,3259730800,3505952657,3345764771,106217008,3516065817,3606008344,3600352804,1432725776,4094571909,1467031594,275423344,851169720,430227734,3100823752,506948616,1363258195,659060556,3750685593,883997877,3785050280,958139571,3318307427,1322822218,3812723403,1537002063,2003034995,1747873779,3602036899,1955562222,1575990012,2024104815,1125592928,2227730452,2716904306,2361852424,442776044,2428436474,593698344,2756734187,3733110249,3204031479,2999351573,3329325298,3815920427,3391569614,3928383900,3515267271,566280711,3940187606,3454069534,4118630271,4000239992,116418474,1914138554,174292421,2731055270,289380356,3203993006,460393269,320620315,685471733,587496836,852142971,1086792851,1017036298,365543100,1126000580,2618297676,1288033470,3409855158,1501505948,4234509866,1607167915,987167468,1816402316,1246189591],Ah=new Array(160),yh(Sh,wh),Sh.prototype.init=function(){return this._ah=1779033703,this._bh=3144134277,this._ch=1013904242,this._dh=2773480762,this._eh=1359893119,this._fh=2600822924,this._gh=528734635,this._hh=1541459225,this._al=4089235720,this._bl=2227873595,this._cl=4271175723,this._dl=1595750129,this._el=2917565137,this._fl=725511199,this._gl=4215389547,this._hl=327033209,this},Sh.prototype._update=function(t){for(var e=this._w,r=0|this._ah,i=0|this._bh,n=0|this._ch,o=0|this._dh,s=0|this._eh,a=0|this._fh,h=0|this._gh,u=0|this._hh,f=0|this._al,l=0|this._bl,d=0|this._cl,c=0|this._dl,p=0|this._el,m=0|this._fl,g=0|this._gl,v=0|this._hl,b=0;b<32;b+=2)e[b]=t.readInt32BE(4*b),e[b+1]=t.readInt32BE(4*b+4);for(;b<160;b+=2){var y=e[b-30],w=e[b-30+1],M=xh(y,w),_=Ih(w,y),A=Ch(y=e[b-4],w=e[b-4+1]),E=Ph(w,y),S=e[b-14],k=e[b-14+1],B=e[b-32],R=e[b-32+1],T=_+k|0,x=M+S+Dh(T,_)|0;x=(x=x+A+Dh(T=T+E|0,E)|0)+B+Dh(T=T+R|0,R)|0,e[b]=x,e[b+1]=T}for(var I=0;I<160;I+=2){x=e[I],T=e[I+1];var C=Bh(r,i,n),P=Bh(f,l,d),D=Rh(r,f),L=Rh(f,r),O=Th(s,p),j=Th(p,s),U=_h[I],N=_h[I+1],z=kh(s,a,h),q=kh(p,m,g),F=v+j|0,Z=u+O+Dh(F,v)|0;Z=(Z=(Z=Z+z+Dh(F=F+q|0,q)|0)+U+Dh(F=F+N|0,N)|0)+x+Dh(F=F+T|0,T)|0;var H=L+P|0,K=D+C+Dh(H,L)|0;u=h,v=g,h=a,g=m,a=s,m=p,s=o+Z+Dh(p=c+F|0,c)|0,o=n,c=d,n=i,d=l,i=r,l=f,r=Z+K+Dh(f=F+H|0,F)|0}this._al=this._al+f|0,this._bl=this._bl+l|0,this._cl=this._cl+d|0,this._dl=this._dl+c|0,this._el=this._el+p|0,this._fl=this._fl+m|0,this._gl=this._gl+g|0,this._hl=this._hl+v|0,this._ah=this._ah+r+Dh(this._al,f)|0,this._bh=this._bh+i+Dh(this._bl,l)|0,this._ch=this._ch+n+Dh(this._cl,d)|0,this._dh=this._dh+o+Dh(this._dl,c)|0,this._eh=this._eh+s+Dh(this._el,p)|0,this._fh=this._fh+a+Dh(this._fl,m)|0,this._gh=this._gh+h+Dh(this._gl,g)|0,this._hh=this._hh+u+Dh(this._hl,v)|0},Sh.prototype._hash=function(){var t=Mh.allocUnsafe(64);function e(e,r,i){t.writeInt32BE(e,i),t.writeInt32BE(r,i+4)}return e(this._ah,this._al,0),e(this._bh,this._bl,8),e(this._ch,this._cl,16),e(this._dh,this._dl,24),e(this._eh,this._el,32),e(this._fh,this._fl,40),e(this._gh,this._gl,48),e(this._hh,this._hl,56),t},bh=Sh),bh}var Oh,jh,Uh,Nh,zh,qh=!1;function Fh(){this.init(),this._w=zh,ka().call(this,128,112)}function Zh(){return qh||(qh=!0,Oh={},jh=Ar(),Uh=Lh(),ka(),Nh=lr().Buffer,zh=new Array(160),jh(Fh,Uh),Fh.prototype.init=function(){return this._ah=3418070365,this._bh=1654270250,this._ch=2438529370,this._dh=355462360,this._eh=1731405415,this._fh=2394180231,this._gh=3675008525,this._hh=1203062813,this._al=3238371032,this._bl=914150663,this._cl=812702999,this._dl=4144912697,this._el=4290775857,this._fl=1750603025,this._gl=1694076839,this._hl=3204075428,this},Fh.prototype._hash=function(){var t=Nh.allocUnsafe(48);function e(e,r,i){t.writeInt32BE(e,i),t.writeInt32BE(r,i+4)}return e(this._ah,this._al,0),e(this._bh,this._bl,8),e(this._ch,this._cl,16),e(this._dh,this._dl,24),e(this._eh,this._el,32),e(this._fh,this._fl,40),t},Oh=Fh),Oh}var Hh,Kh,Wh=!1;function Gh(){return Wh||(Wh=!0,Hh={},(Kh=Hh=function(t){t=t.toLowerCase();var e=Kh[t];if(!e)throw new Error(t+" is not supported (we accept pull requests)");return new e}).sha=ja(),Kh.sha1=Va(),Kh.sha224=vh(),Kh.sha256=uh(),Kh.sha384=Zh(),Kh.sha512=Lh()),Hh}var Yh,Vh,$h,Xh,Jh=!1;function Qh(t){$h.call(this),this.hashMode="string"==typeof t,this.hashMode?this[t]=this._finalOrDigest:this.final=this._finalOrDigest,this._final&&(this.__final=this._final,this._final=null),this._decoder=null,this._encoding=null}function tu(){return Jh||(Jh=!0,Yh={},Vh=lr().Buffer,$h=Ls().Transform,Xh=In().StringDecoder,Ar()(Qh,$h),Qh.prototype.update=function(t,e,r){"string"==typeof t&&(t=Vh.from(t,e));var i=this._update(t);return this.hashMode?this:(r&&(i=this._toString(i,r)),i)},Qh.prototype.setAutoPadding=function(){},Qh.prototype.getAuthTag=function(){throw new Error("trying to get auth tag in unsupported state")},Qh.prototype.setAuthTag=function(){throw new Error("trying to set auth tag in unsupported state")},Qh.prototype.setAAD=function(){throw new Error("trying to set aad in unsupported state")},Qh.prototype._transform=function(t,e,r){var i;try{this.hashMode?this._update(t):this.push(this._update(t))}catch(t){i=t}finally{r(i)}},Qh.prototype._flush=function(t){var e;try{this.push(this.__final())}catch(t){e=t}t(e)},Qh.prototype._finalOrDigest=function(t){var e=this.__final()||Vh.alloc(0);return t&&(e=this._toString(e,t,!0)),e},Qh.prototype._toString=function(t,e,r){if(this._decoder||(this._decoder=new Xh(e),this._encoding=e),this._encoding!==e)throw new Error("can't switch encodings");var i=this._decoder.write(t);return r&&(i+=this._decoder.end()),i},Yh=Qh),Yh}var eu,ru,iu,nu,ou,su,au=!1;function hu(t){su.call(this,"digest"),this._hash=t}function uu(){return au||(au=!0,eu={},ru=Ar(),iu=ea(),nu=Ma(),ou=Gh(),su=tu(),ru(hu,su),hu.prototype._update=function(t){this._hash.update(t)},hu.prototype._final=function(){return this._hash.digest()},eu=function(t){return"md5"===(t=t.toLowerCase())?new iu:"rmd160"===t||"ripemd160"===t?new nu:new hu(ou(t))}),eu}var fu,lu,du,cu,pu,mu,gu=!1;function vu(t,e){cu.call(this,"digest"),"string"==typeof e&&(e=du.from(e)),this._alg=t,this._key=e,e.length>mu?e=t(e):e.lengthr)?e=("rmd160"===t?new Tu:xu(t)).update(e).digest():e.lengthFu||e!=e)throw new TypeError("Bad key length")}),qu}var Ku,Wu,Gu,Yu=!1;function Vu(){return Yu||(Yu=!0,Ku={},jt(),t.process&&t.process.browser?Wu="utf-8":t.process&&t.process.version?(Gu=parseInt(jt().version.split(".")[0].slice(1),10),Wu=Gu>=6?"utf-8":"binary"):Wu="utf-8",Ku=Wu),Ku}var $u,Xu,Ju=!1;function Qu(){return Ju||(Ju=!0,$u={},Xu=lr().Buffer,$u=function(t,e,r){if(Xu.isBuffer(t))return t;if("string"==typeof t)return Xu.from(t,e);if(ArrayBuffer.isView(t))return Xu.from(t.buffer);throw new TypeError(r+" must be a string, a Buffer, a typed array or a DataView")}),$u}var tf,ef,rf,nf,of,sf,af,hf,uf,ff,lf=!1;function df(t,e,r){var i=function(t){function e(e){return nf(t).update(e).digest()}function r(t){return(new rf).update(t).digest()}return"rmd160"===t||"ripemd160"===t?r:"md5"===t?ef:e}(t),n="sha512"===t||"sha384"===t?128:64;e.length>n?e=i(e):e.length>>0},jf=function(t,e,r){t[0+r]=e>>>24,t[1+r]=e>>>16&255,t[2+r]=e>>>8&255,t[3+r]=255&e},Of.writeUInt32BE=jf,Uf=function(t,e,r,i){for(var n=0,o=0,s=6;s>=0;s-=2){for(var a=0;a<=24;a+=8)n<<=1,n|=e>>>a+s&1;for(a=0;a<=24;a+=8)n<<=1,n|=t>>>a+s&1}for(s=6;s>=0;s-=2){for(a=1;a<=25;a+=8)o<<=1,o|=e>>>a+s&1;for(a=1;a<=25;a+=8)o<<=1,o|=t>>>a+s&1}r[i+0]=n>>>0,r[i+1]=o>>>0},Of.ip=Uf,Nf=function(t,e,r,i){for(var n=0,o=0,s=0;s<4;s++)for(var a=24;a>=0;a-=8)n<<=1,n|=e>>>a+s&1,n<<=1,n|=t>>>a+s&1;for(s=4;s<8;s++)for(a=24;a>=0;a-=8)o<<=1,o|=e>>>a+s&1,o<<=1,o|=t>>>a+s&1;r[i+0]=n>>>0,r[i+1]=o>>>0},Of.rip=Nf,zf=function(t,e,r,i){for(var n=0,o=0,s=7;s>=5;s--){for(var a=0;a<=24;a+=8)n<<=1,n|=e>>a+s&1;for(a=0;a<=24;a+=8)n<<=1,n|=t>>a+s&1}for(a=0;a<=24;a+=8)n<<=1,n|=e>>a+s&1;for(s=1;s<=3;s++){for(a=0;a<=24;a+=8)o<<=1,o|=e>>a+s&1;for(a=0;a<=24;a+=8)o<<=1,o|=t>>a+s&1}for(a=0;a<=24;a+=8)o<<=1,o|=t>>a+s&1;r[i+0]=n>>>0,r[i+1]=o>>>0},Of.pc1=zf,qf=function(t,e){return t<>>28-e},Of.r28shl=qf,Ff=[14,11,17,4,27,23,25,0,13,22,7,18,5,9,16,24,2,20,12,21,1,8,15,26,15,4,25,19,9,1,26,16,5,11,23,8,12,7,17,0,22,3,10,14,6,20,27,24],Zf=function(t,e,r,i){for(var n=0,o=0,s=Ff.length>>>1,a=0;a>>Ff[a]&1;for(a=s;a>>Ff[a]&1;r[i+0]=n>>>0,r[i+1]=o>>>0},Of.pc2=Zf,Hf=function(t,e,r){var i=0,n=0;i=(1&t)<<5|t>>>27;for(var o=23;o>=15;o-=4)i<<=6,i|=t>>>o&63;for(o=11;o>=3;o-=4)n|=t>>>o&63,n<<=6;n|=(31&t)<<1|t>>>31,e[r+0]=i>>>0,e[r+1]=n>>>0},Of.expand=Hf,Kf=[14,0,4,15,13,7,1,4,2,14,15,2,11,13,8,1,3,10,10,6,6,12,12,11,5,9,9,5,0,3,7,8,4,15,1,12,14,8,8,2,13,4,6,9,2,1,11,7,15,5,12,11,9,3,7,14,3,10,10,0,5,6,0,13,15,3,1,13,8,4,14,7,6,15,11,2,3,8,4,14,9,12,7,0,2,1,13,10,12,6,0,9,5,11,10,5,0,13,14,8,7,10,11,1,10,3,4,15,13,4,1,2,5,11,8,6,12,7,6,12,9,0,3,5,2,14,15,9,10,13,0,7,9,0,14,9,6,3,3,4,15,6,5,10,1,2,13,8,12,5,7,14,11,12,4,11,2,15,8,1,13,1,6,10,4,13,9,0,8,6,15,9,3,8,0,7,11,4,1,15,2,14,12,3,5,11,10,5,14,2,7,12,7,13,13,8,14,11,3,5,0,6,6,15,9,0,10,3,1,4,2,7,8,2,5,12,11,1,12,10,4,14,15,9,10,3,6,15,9,0,0,6,12,10,11,1,7,13,13,8,15,9,1,4,3,5,14,11,5,12,2,7,8,2,4,14,2,14,12,11,4,2,1,12,7,4,10,7,11,13,6,1,8,5,5,0,3,15,15,10,13,3,0,9,14,8,9,6,4,11,2,8,1,12,11,7,10,1,13,14,7,2,8,13,15,6,9,15,12,0,5,9,6,10,3,4,0,5,14,3,12,10,1,15,10,4,15,2,9,7,2,12,6,9,8,5,0,6,13,1,3,13,4,14,14,0,7,11,5,3,11,8,9,4,14,3,15,2,5,12,2,9,8,5,12,15,3,10,7,11,0,14,4,1,10,7,1,6,13,0,11,8,6,13,4,13,11,0,2,11,14,7,15,4,0,9,8,1,13,10,3,14,12,3,9,5,7,12,5,2,10,15,6,8,1,6,1,6,4,11,11,13,13,8,12,1,3,4,7,10,14,7,10,9,15,5,6,0,8,15,0,14,5,2,9,3,2,12,13,1,2,15,8,13,4,8,6,10,15,3,11,7,1,4,10,12,9,5,3,6,14,11,5,0,0,14,12,9,7,2,7,2,11,1,4,14,1,7,9,4,12,10,14,8,2,13,0,15,6,12,10,9,13,0,15,3,3,5,5,6,8,11],Wf=function(t,e){for(var r=0,i=0;i<4;i++)r<<=4,r|=Kf[64*i+(t>>>18-6*i&63)];for(i=0;i<4;i++)r<<=4,r|=Kf[256+64*i+(e>>>18-6*i&63)];return r>>>0},Of.substitute=Wf,Gf=[16,25,12,11,3,20,4,15,31,17,9,6,27,14,1,22,30,24,8,18,0,5,29,23,13,19,2,26,10,21,28,7],Yf=function(t){for(var e=0,r=0;r>>Gf[r]&1;return e>>>0},Of.permute=Yf,Vf=function(t,e,r){for(var i=t.toString(2);i.length0;i--)e+=this._buffer(t,e),r+=this._flushBuffer(n,r);return e+=this._buffer(t,e),n},nl.prototype.final=function(t){var e,r;return t&&(e=this.update(t)),r="encrypt"===this.type?this._finalEncrypt():this._finalDecrypt(),e?e.concat(r):r},nl.prototype._pad=function(t,e){if(0===e)return!1;for(;e>>1];r=Xf().r28shl(r,o),i=Xf().r28shl(i,o),Xf().pc2(r,i,t.keys,n)}},dl.prototype._update=function(t,e,r,i){var n=this._desState,o=Xf().readUInt32BE(t,e),s=Xf().readUInt32BE(t,e+4);Xf().ip(o,s,n.tmp,0),o=n.tmp[0],s=n.tmp[1],"encrypt"===this.type?this._encrypt(n,o,s,n.tmp,0):this._decrypt(n,o,s,n.tmp,0),o=n.tmp[0],s=n.tmp[1],Xf().writeUInt32BE(r,o,i),Xf().writeUInt32BE(r,s,i+4)},dl.prototype._pad=function(t,e){for(var r=t.length-e,i=e;i>>0,o=l}Xf().rip(s,o,i,n)},dl.prototype._decrypt=function(t,e,r,i,n){for(var o=r,s=e,a=t.keys.length-2;a>=0;a-=2){var h=t.keys[a],u=t.keys[a+1];Xf().expand(o,t.tmp,0),h^=t.tmp[0],u^=t.tmp[1];var f=Xf().substitute(h,u),l=o;o=(s^Xf().permute(f))>>>0,s=l}Xf().rip(o,s,i,n)}),sl}var pl,ml,gl,vl,bl=!1;function yl(t){el().equal(t.length,8,"Invalid IV length"),this.iv=new Array(8);for(var e=0;e>o%8,t._prev=Ad(t._prev,r?i:n);return s}function Ad(t,e){var r=t.length,i=-1,n=yd.allocUnsafe(t.length);for(t=yd.concat([t,yd.from([e])]);++i>7;return n}function Ed(){return Md||(Md=!0,bd={},yd=lr().Buffer,wd=function(t,e,r){for(var i=e.length,n=yd.allocUnsafe(i),o=-1;++o>>24]^f[p>>>16&255]^l[m>>>8&255]^d[255&g]^e[v++],s=u[p>>>24]^f[m>>>16&255]^l[g>>>8&255]^d[255&c]^e[v++],a=u[m>>>24]^f[g>>>16&255]^l[c>>>8&255]^d[255&p]^e[v++],h=u[g>>>24]^f[c>>>16&255]^l[p>>>8&255]^d[255&m]^e[v++],c=o,p=s,m=a,g=h;return o=(i[c>>>24]<<24|i[p>>>16&255]<<16|i[m>>>8&255]<<8|i[255&g])^e[v++],s=(i[p>>>24]<<24|i[m>>>16&255]<<16|i[g>>>8&255]<<8|i[255&c])^e[v++],a=(i[m>>>24]<<24|i[g>>>16&255]<<16|i[c>>>8&255]<<8|i[255&p])^e[v++],h=(i[g>>>24]<<24|i[c>>>16&255]<<16|i[p>>>8&255]<<8|i[255&m])^e[v++],[o>>>=0,s>>>=0,a>>>=0,h>>>=0]}function hc(t){this._key=oc(t),this._reset()}function uc(){return nc||(nc=!0,Qd={},tc=lr().Buffer,ec=[0,1,2,4,8,16,32,64,128,27,54],rc=function(){for(var t=new Array(256),e=0;e<256;e++)t[e]=e<128?e<<1:e<<1^283;for(var r=[],i=[],n=[[],[],[],[]],o=[[],[],[],[]],s=0,a=0,h=0;h<256;++h){var u=a^a<<1^a<<2^a<<3^a<<4;u=u>>>8^255&u^99,r[s]=u,i[u]=s;var f=t[s],l=t[f],d=t[l],c=257*t[u]^16843008*u;n[0][s]=c<<24|c>>>8,n[1][s]=c<<16|c>>>16,n[2][s]=c<<8|c>>>24,n[3][s]=c,c=16843009*d^65537*l^257*f^16843008*s,o[0][u]=c<<24|c>>>8,o[1][u]=c<<16|c>>>16,o[2][u]=c<<8|c>>>24,o[3][u]=c,0===s?s=a=1:(s=f^t[t[t[d^f]]],a^=t[t[a]])}return{SBOX:r,INV_SBOX:i,SUB_MIX:n,INV_SUB_MIX:o}}(),hc.blockSize=16,hc.keySize=32,hc.prototype.blockSize=hc.blockSize,hc.prototype.keySize=hc.keySize,hc.prototype._reset=function(){for(var t=this._key,e=t.length,r=e+6,i=4*(r+1),n=[],o=0;o>>24,s=rc.SBOX[s>>>24]<<24|rc.SBOX[s>>>16&255]<<16|rc.SBOX[s>>>8&255]<<8|rc.SBOX[255&s],s^=ec[o/e|0]<<24):e>6&&o%e==4&&(s=rc.SBOX[s>>>24]<<24|rc.SBOX[s>>>16&255]<<16|rc.SBOX[s>>>8&255]<<8|rc.SBOX[255&s]),n[o]=n[o-e]^s}for(var a=[],h=0;h>>24]]^rc.INV_SUB_MIX[1][rc.SBOX[f>>>16&255]]^rc.INV_SUB_MIX[2][rc.SBOX[f>>>8&255]]^rc.INV_SUB_MIX[3][rc.SBOX[255&f]]}this._nRounds=r,this._keySchedule=n,this._invKeySchedule=a},hc.prototype.encryptBlockRaw=function(t){return ac(t=oc(t),this._keySchedule,rc.SUB_MIX,rc.SBOX,this._nRounds)},hc.prototype.encryptBlock=function(t){var e=this.encryptBlockRaw(t),r=tc.allocUnsafe(16);return r.writeUInt32BE(e[0],0),r.writeUInt32BE(e[1],4),r.writeUInt32BE(e[2],8),r.writeUInt32BE(e[3],12),r},hc.prototype.decryptBlock=function(t){var e=(t=oc(t))[1];t[1]=t[3],t[3]=e;var r=ac(t,this._invKeySchedule,rc.INV_SUB_MIX,rc.INV_SBOX,this._nRounds),i=tc.allocUnsafe(16);return i.writeUInt32BE(r[0],0),i.writeUInt32BE(r[3],4),i.writeUInt32BE(r[2],8),i.writeUInt32BE(r[1],12),i},hc.prototype.scrub=function(){sc(this._keySchedule),sc(this._invKeySchedule),sc(this._key)},ic=hc,Qd.AES=ic),Qd}var fc,lc,dc,cc=!1;function pc(t){var e=lc.allocUnsafe(16);return e.writeUInt32BE(t[0]>>>0,0),e.writeUInt32BE(t[1]>>>0,4),e.writeUInt32BE(t[2]>>>0,8),e.writeUInt32BE(t[3]>>>0,12),e}function mc(t){this.h=t,this.state=lc.alloc(16,0),this.cache=lc.allocUnsafe(0)}function gc(){return cc||(cc=!0,fc={},lc=lr().Buffer,dc=lc.alloc(16,0),mc.prototype.ghash=function(t){for(var e=-1;++e0;e--)i[e]=i[e]>>>1|(1&i[e-1])<<31;i[0]=i[0]>>>1,r&&(i[0]=i[0]^225<<24)}this.state=pc(n)},mc.prototype.update=function(t){var e;for(this.cache=lc.concat([this.cache,t]);this.cache.length>=16;)e=this.cache.slice(0,16),this.cache=this.cache.slice(16),this.ghash(e)},mc.prototype.final=function(t,e){return this.cache.length&&this.ghash(lc.concat([this.cache,dc],16)),this.ghash(pc([0,t,0,e])),this.state},fc=mc),fc}var vc,bc,yc,wc,Mc,_c,Ac,Ec=!1;function Sc(t,e,r,i){yc.call(this);var n=bc.alloc(4,0);this._cipher=new(uc().AES)(e);var o=this._cipher.encryptBlock(n);this._ghash=new Mc(o),r=function(t,e,r){if(12===e.length)return t._finID=bc.concat([e,bc.from([0,0,0,1])]),bc.concat([e,bc.from([0,0,0,2])]);var i=new Mc(r),n=e.length,o=n%16;i.update(e),o&&(o=16-o,i.update(bc.alloc(o,0))),i.update(bc.alloc(8,0));var s=8*n,a=bc.alloc(8);a.writeUIntBE(s,0,8),i.update(a),t._finID=i.state;var h=bc.from(t._finID);return Ac(h),h}(this,r,o),this._prev=bc.from(r),this._cache=bc.allocUnsafe(0),this._secCache=bc.allocUnsafe(0),this._decrypt=i,this._alen=0,this._len=0,this._mode=t,this._authTag=null,this._called=!1}function kc(){vc={},uc(),bc=lr().Buffer,yc=tu(),wc=Ar(),Mc=gc(),_c=Jl(),Ac=Ld(),wc(Sc,yc),Sc.prototype._update=function(t){if(!this._called&&this._alen){var e=16-this._alen%16;e<16&&(e=bc.alloc(e,0),this._ghash.update(e))}this._called=!0;var r=this._mode.encrypt(this,t);return this._decrypt?this._ghash.update(t):this._ghash.update(r),this._len+=t.length,r},Sc.prototype._final=function(){if(this._decrypt&&!this._authTag)throw new Error("Unsupported state or unable to authenticate data");var t=_c(this._ghash.final(8*this._alen,8*this._len),this._cipher.encryptBlock(this._finID));if(this._decrypt&&function(t,e){var r=0;t.length!==e.length&&r++;for(var i=Math.min(t.length,e.length),n=0;n0||i>0;){var h=new Oc;h.update(a),h.update(t),e&&h.update(e),a=h.digest();var u=0;if(n>0){var f=o.length-n;u=Math.min(n,a.length),a.copy(o,f,0,u),n-=u}if(u0){var l=s.length-i,d=Math.min(i,a.length-u);a.copy(s,l,u,u+d),i-=d}}return a.fill(0),{key:o,iv:s}}function Nc(){return jc||(jc=!0,Dc={},Lc=lr().Buffer,Oc=ea(),Dc=Uc),Dc}var zc,qc,Fc,Zc,Hc,Kc,Wc,Gc,Yc,Vc,$c=!1;function Xc(t,e,r){Kc.call(this),this._cache=new Jc,this._cipher=new(uc().AES)(e),this._prev=Zc.from(r),this._mode=t,this._autopadding=!0}function Jc(){this.cache=Zc.allocUnsafe(0)}function Qc(t,e,r){var i=qc[t.toLowerCase()];if(!i)throw new TypeError("invalid suite type");if("string"==typeof e&&(e=Zc.from(e)),e.length!==i.key/8)throw new TypeError("invalid key length "+e.length);if("string"==typeof r&&(r=Zc.from(r)),"GCM"!==i.mode&&r.length!==i.iv)throw new TypeError("invalid iv length "+r.length);return"stream"===i.type?new Hc(i.module,e,r):"auth"===i.type?new Fc(i.module,e,r):new Xc(i.module,e,r)}function tp(t,e){var r=qc[t.toLowerCase()];if(!r)throw new TypeError("invalid suite type");var i=Wc(e,!1,r.key,r.iv);return Qc(t,i.key,i.iv)}function ep(){return $c||($c=!0,zc={},qc=Jd(),Fc=Bc(),Zc=lr().Buffer,Hc=Pc(),Kc=tu(),uc(),Wc=Nc(),Ar()(Xc,Kc),Xc.prototype._update=function(t){var e,r;this._cache.add(t);for(var i=[];e=this._cache.get();)r=this._mode.encrypt(this,e),i.push(r);return Zc.concat(i)},Gc=Zc.alloc(16,16),Xc.prototype._final=function(){var t=this._cache.flush();if(this._autopadding)return t=this._mode.encrypt(this,t),this._cipher.scrub(),t;if(!t.equals(Gc))throw this._cipher.scrub(),new Error("data not multiple of block length")},Xc.prototype.setAutoPadding=function(t){return this._autopadding=!!t,this},Jc.prototype.add=function(t){this.cache=Zc.concat([this.cache,t])},Jc.prototype.get=function(){if(this.cache.length>15){var t=this.cache.slice(0,16);return this.cache=this.cache.slice(16),t}return null},Jc.prototype.flush=function(){for(var t=16-this.cache.length,e=Zc.allocUnsafe(t),r=-1;++r16)throw new Error("unable to decrypt data");for(var r=-1;++r16)return e=this.cache.slice(0,16),this.cache=this.cache.slice(16),e}else if(this.cache.length>=16)return e=this.cache.slice(0,16),this.cache=this.cache.slice(16),e;return null},cp.prototype.flush=function(){if(this.cache.length)return this.cache},up=mp,rp.createDecipher=up,fp=pp,rp.createDecipheriv=fp}function vp(){return lp||(lp=!0,gp()),rp}var bp,yp,wp,Mp,_p,Ap,Ep,Sp,kp,Bp,Rp,Tp,xp=!1;function Ip(){return Object.keys(Ep)}function Cp(){return xp||(xp=!0,Ap={},ep(),vp(),Ep=Wd(),bp=ep().createCipher,Sp=Ap.Cipher=bp,Ap.createCipher=Sp,yp=ep().createCipheriv,kp=Ap.Cipheriv=yp,Ap.createCipheriv=kp,wp=vp().createDecipher,Bp=Ap.Decipher=wp,Ap.createDecipher=Bp,Mp=vp().createDecipheriv,Rp=Ap.Decipheriv=Mp,Ap.createDecipheriv=Rp,_p=Ip,Tp=Ap.getCiphers=_p,Ap.listCiphers=Tp),Ap}var Pp,Dp,Lp,Op,jp,Up,Np,zp,qp=!1;function Fp(){return qp||(qp=!0,(Lp={})["des-ecb"]={key:8,iv:0},Pp={key:8,iv:8},Op=Lp.des=Pp,Lp["des-cbc"]=Op,Dp={key:24,iv:8},jp=Lp.des3=Dp,Lp["des-ede3-cbc"]=jp,Up={key:24,iv:0},Lp["des-ede3"]=Up,Np={key:16,iv:8},Lp["des-ede-cbc"]=Np,zp={key:16,iv:0},Lp["des-ede"]=zp),Lp}var Zp,Hp,Kp,Wp,Gp,Yp,Vp,$p,Xp,Jp,Qp,tm,em,rm,im,nm=!1;function om(t,e){var r,i;if(t=t.toLowerCase(),$p[t])r=$p[t].key,i=$p[t].iv;else{if(!Xp[t])throw new TypeError("invalid suite type");r=8*Xp[t].key,i=Xp[t].iv}var n=Jp(e,!1,r,i);return am(t,n.key,n.iv)}function sm(t,e){var r,i;if(t=t.toLowerCase(),$p[t])r=$p[t].key,i=$p[t].iv;else{if(!Xp[t])throw new TypeError("invalid suite type");r=8*Xp[t].key,i=Xp[t].iv}var n=Jp(e,!1,r,i);return hm(t,n.key,n.iv)}function am(t,e,r){if(t=t.toLowerCase(),$p[t])return Cp().createCipheriv(t,e,r);if(Xp[t])return new Vp({key:e,iv:r,mode:t});throw new TypeError("invalid suite type")}function hm(t,e,r){if(t=t.toLowerCase(),$p[t])return Cp().createDecipheriv(t,e,r);if(Xp[t])return new Vp({key:e,iv:r,mode:t,decrypt:!0});throw new TypeError("invalid suite type")}function um(){return Object.keys(Xp).concat(Cp().getCiphers())}function fm(){return nm||(nm=!0,Yp={},Vp=Hl(),Cp(),$p=Jd(),Xp=Fp(),Jp=Nc(),Zp=om,Qp=Yp.Cipher=Zp,Yp.createCipher=Qp,Hp=am,tm=Yp.Cipheriv=Hp,Yp.createCipheriv=tm,Kp=sm,em=Yp.Decipher=Kp,Yp.createDecipher=em,Wp=hm,rm=Yp.Decipheriv=Wp,Yp.createDecipheriv=rm,Gp=um,im=Yp.getCiphers=Gp,Yp.listCiphers=im),Yp}var lm,dm=!1;function cm(){lm=function(){var t={exports:this};return function(t,e){function r(t,e){if(!t)throw new Error(e||"Assertion failed")}function i(t,e){t.super_=e;var r=function(){};r.prototype=e.prototype,t.prototype=new r,t.prototype.constructor=t}function n(t,e,r){if(n.isBN(t))return t;this.negative=0,this.words=null,this.length=0,this.red=null,null!==t&&("le"!==e&&"be"!==e||(r=e,e=10),this._init(t||0,e||10,r||"be"))}var o;"object"==typeof t?t.exports=n:e.BN=n,n.BN=n,n.wordSize=26;try{o="undefined"!=typeof window&&void 0!==window.Buffer?window.Buffer:ir().Buffer}catch(t){}function s(t,e){var r=t.charCodeAt(e);return r>=65&&r<=70?r-55:r>=97&&r<=102?r-87:r-48&15}function a(t,e,r){var i=s(t,r);return r-1>=e&&(i|=s(t,r-1)<<4),i}function h(t,e,r,i){for(var n=0,o=Math.min(t.length,r),s=e;s=49?a-49+10:a>=17?a-17+10:a}return n}n.isBN=function(t){return t instanceof n||null!==t&&"object"==typeof t&&t.constructor.wordSize===n.wordSize&&Array.isArray(t.words)},n.max=function(t,e){return t.cmp(e)>0?t:e},n.min=function(t,e){return t.cmp(e)<0?t:e},n.prototype._init=function(t,e,i){if("number"==typeof t)return this._initNumber(t,e,i);if("object"==typeof t)return this._initArray(t,e,i);"hex"===e&&(e=16),r(e===(0|e)&&e>=2&&e<=36);var n=0;"-"===(t=t.toString().replace(/\s+/g,""))[0]&&(n++,this.negative=1),n=0;n-=3)s=t[n]|t[n-1]<<8|t[n-2]<<16,this.words[o]|=s<>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===i)for(n=0,o=0;n>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this.strip()},n.prototype._parseHex=function(t,e,r){this.length=Math.ceil((t.length-e)/6),this.words=new Array(this.length);for(var i=0;i=e;i-=2)n=a(t,e,i)<=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;else for(i=(t.length-e)%2==0?e+1:e;i=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;this.strip()},n.prototype._parseBase=function(t,e,r){this.words=[0],this.length=1;for(var i=0,n=1;n<=67108863;n*=e)i++;i--,n=n/e|0;for(var o=t.length-r,s=o%i,a=Math.min(o,o-s)+r,u=0,f=r;f1&&0===this.words[this.length-1];)this.length--;return this._normSign()},n.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},n.prototype.inspect=function(){return(this.red?""};var u=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],f=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],l=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];function d(t,e,r){r.negative=e.negative^t.negative;var i=t.length+e.length|0;r.length=i,i=i-1|0;var n=0|t.words[0],o=0|e.words[0],s=n*o,a=67108863&s,h=s/67108864|0;r.words[0]=a;for(var u=1;u>>26,l=67108863&h,d=Math.min(u,e.length-1),c=Math.max(0,u-t.length+1);c<=d;c++){var p=u-c|0;f+=(s=(n=0|t.words[p])*(o=0|e.words[c])+l)/67108864|0,l=67108863&s}r.words[u]=0|l,h=0|f}return 0!==h?r.words[u]=0|h:r.length--,r.strip()}n.prototype.toString=function(t,e){var i;if(e=0|e||1,16===(t=t||10)||"hex"===t){i="";for(var n=0,o=0,s=0;s>>24-n&16777215)||s!==this.length-1?u[6-h.length]+h+i:h+i,(n+=2)>=26&&(n-=26,s--)}for(0!==o&&(i=o.toString(16)+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}if(t===(0|t)&&t>=2&&t<=36){var d=f[t],c=l[t];i="";var p=this.clone();for(p.negative=0;!p.isZero();){var m=p.modn(c).toString(t);i=(p=p.idivn(c)).isZero()?m+i:u[d-m.length]+m+i}for(this.isZero()&&(i="0"+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}r(!1,"Base should be between 2 and 36")},n.prototype.toNumber=function(){var t=this.words[0];return 2===this.length?t+=67108864*this.words[1]:3===this.length&&1===this.words[2]?t+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-t:t},n.prototype.toJSON=function(){return this.toString(16)},n.prototype.toBuffer=function(t,e){return r(void 0!==o),this.toArrayLike(o,t,e)},n.prototype.toArray=function(t,e){return this.toArrayLike(Array,t,e)},n.prototype.toArrayLike=function(t,e,i){var n=this.byteLength(),o=i||Math.max(1,n);r(n<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0"),this.strip();var s,a,h="le"===e,u=new t(o),f=this.clone();if(h){for(a=0;!f.isZero();a++)s=f.andln(255),f.iushrn(8),u[a]=s;for(;a=4096&&(r+=13,e>>>=13),e>=64&&(r+=7,e>>>=7),e>=8&&(r+=4,e>>>=4),e>=2&&(r+=2,e>>>=2),r+e},n.prototype._zeroBits=function(t){if(0===t)return 26;var e=t,r=0;return 0==(8191&e)&&(r+=13,e>>>=13),0==(127&e)&&(r+=7,e>>>=7),0==(15&e)&&(r+=4,e>>>=4),0==(3&e)&&(r+=2,e>>>=2),0==(1&e)&&r++,r},n.prototype.bitLength=function(){var t=this.words[this.length-1],e=this._countBits(t);return 26*(this.length-1)+e},n.prototype.zeroBits=function(){if(this.isZero())return 0;for(var t=0,e=0;et.length?this.clone().ior(t):t.clone().ior(this)},n.prototype.uor=function(t){return this.length>t.length?this.clone().iuor(t):t.clone().iuor(this)},n.prototype.iuand=function(t){var e;e=this.length>t.length?t:this;for(var r=0;rt.length?this.clone().iand(t):t.clone().iand(this)},n.prototype.uand=function(t){return this.length>t.length?this.clone().iuand(t):t.clone().iuand(this)},n.prototype.iuxor=function(t){var e,r;this.length>t.length?(e=this,r=t):(e=t,r=this);for(var i=0;it.length?this.clone().ixor(t):t.clone().ixor(this)},n.prototype.uxor=function(t){return this.length>t.length?this.clone().iuxor(t):t.clone().iuxor(this)},n.prototype.inotn=function(t){r("number"==typeof t&&t>=0);var e=0|Math.ceil(t/26),i=t%26;this._expand(e),i>0&&e--;for(var n=0;n0&&(this.words[n]=~this.words[n]&67108863>>26-i),this.strip()},n.prototype.notn=function(t){return this.clone().inotn(t)},n.prototype.setn=function(t,e){r("number"==typeof t&&t>=0);var i=t/26|0,n=t%26;return this._expand(i+1),this.words[i]=e?this.words[i]|1<t.length?(r=this,i=t):(r=t,i=this);for(var n=0,o=0;o>>26;for(;0!==n&&o>>26;if(this.length=r.length,0!==n)this.words[this.length]=n,this.length++;else if(r!==this)for(;ot.length?this.clone().iadd(t):t.clone().iadd(this)},n.prototype.isub=function(t){if(0!==t.negative){t.negative=0;var e=this.iadd(t);return t.negative=1,e._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(t),this.negative=1,this._normSign();var r,i,n=this.cmp(t);if(0===n)return this.negative=0,this.length=1,this.words[0]=0,this;n>0?(r=this,i=t):(r=t,i=this);for(var o=0,s=0;s>26,this.words[s]=67108863&e;for(;0!==o&&s>26,this.words[s]=67108863&e;if(0===o&&s>>13,c=0|s[1],p=8191&c,m=c>>>13,g=0|s[2],v=8191&g,b=g>>>13,y=0|s[3],w=8191&y,M=y>>>13,_=0|s[4],A=8191&_,E=_>>>13,S=0|s[5],k=8191&S,B=S>>>13,R=0|s[6],T=8191&R,x=R>>>13,I=0|s[7],C=8191&I,P=I>>>13,D=0|s[8],L=8191&D,O=D>>>13,j=0|s[9],U=8191&j,N=j>>>13,z=0|a[0],q=8191&z,F=z>>>13,Z=0|a[1],H=8191&Z,K=Z>>>13,W=0|a[2],G=8191&W,Y=W>>>13,V=0|a[3],$=8191&V,X=V>>>13,J=0|a[4],Q=8191&J,tt=J>>>13,et=0|a[5],rt=8191&et,it=et>>>13,nt=0|a[6],ot=8191&nt,st=nt>>>13,at=0|a[7],ht=8191&at,ut=at>>>13,ft=0|a[8],lt=8191&ft,dt=ft>>>13,ct=0|a[9],pt=8191&ct,mt=ct>>>13;r.negative=t.negative^e.negative,r.length=19;var gt=(u+(i=Math.imul(l,q))|0)+((8191&(n=(n=Math.imul(l,F))+Math.imul(d,q)|0))<<13)|0;u=((o=Math.imul(d,F))+(n>>>13)|0)+(gt>>>26)|0,gt&=67108863,i=Math.imul(p,q),n=(n=Math.imul(p,F))+Math.imul(m,q)|0,o=Math.imul(m,F);var vt=(u+(i=i+Math.imul(l,H)|0)|0)+((8191&(n=(n=n+Math.imul(l,K)|0)+Math.imul(d,H)|0))<<13)|0;u=((o=o+Math.imul(d,K)|0)+(n>>>13)|0)+(vt>>>26)|0,vt&=67108863,i=Math.imul(v,q),n=(n=Math.imul(v,F))+Math.imul(b,q)|0,o=Math.imul(b,F),i=i+Math.imul(p,H)|0,n=(n=n+Math.imul(p,K)|0)+Math.imul(m,H)|0,o=o+Math.imul(m,K)|0;var bt=(u+(i=i+Math.imul(l,G)|0)|0)+((8191&(n=(n=n+Math.imul(l,Y)|0)+Math.imul(d,G)|0))<<13)|0;u=((o=o+Math.imul(d,Y)|0)+(n>>>13)|0)+(bt>>>26)|0,bt&=67108863,i=Math.imul(w,q),n=(n=Math.imul(w,F))+Math.imul(M,q)|0,o=Math.imul(M,F),i=i+Math.imul(v,H)|0,n=(n=n+Math.imul(v,K)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,K)|0,i=i+Math.imul(p,G)|0,n=(n=n+Math.imul(p,Y)|0)+Math.imul(m,G)|0,o=o+Math.imul(m,Y)|0;var yt=(u+(i=i+Math.imul(l,$)|0)|0)+((8191&(n=(n=n+Math.imul(l,X)|0)+Math.imul(d,$)|0))<<13)|0;u=((o=o+Math.imul(d,X)|0)+(n>>>13)|0)+(yt>>>26)|0,yt&=67108863,i=Math.imul(A,q),n=(n=Math.imul(A,F))+Math.imul(E,q)|0,o=Math.imul(E,F),i=i+Math.imul(w,H)|0,n=(n=n+Math.imul(w,K)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,K)|0,i=i+Math.imul(v,G)|0,n=(n=n+Math.imul(v,Y)|0)+Math.imul(b,G)|0,o=o+Math.imul(b,Y)|0,i=i+Math.imul(p,$)|0,n=(n=n+Math.imul(p,X)|0)+Math.imul(m,$)|0,o=o+Math.imul(m,X)|0;var wt=(u+(i=i+Math.imul(l,Q)|0)|0)+((8191&(n=(n=n+Math.imul(l,tt)|0)+Math.imul(d,Q)|0))<<13)|0;u=((o=o+Math.imul(d,tt)|0)+(n>>>13)|0)+(wt>>>26)|0,wt&=67108863,i=Math.imul(k,q),n=(n=Math.imul(k,F))+Math.imul(B,q)|0,o=Math.imul(B,F),i=i+Math.imul(A,H)|0,n=(n=n+Math.imul(A,K)|0)+Math.imul(E,H)|0,o=o+Math.imul(E,K)|0,i=i+Math.imul(w,G)|0,n=(n=n+Math.imul(w,Y)|0)+Math.imul(M,G)|0,o=o+Math.imul(M,Y)|0,i=i+Math.imul(v,$)|0,n=(n=n+Math.imul(v,X)|0)+Math.imul(b,$)|0,o=o+Math.imul(b,X)|0,i=i+Math.imul(p,Q)|0,n=(n=n+Math.imul(p,tt)|0)+Math.imul(m,Q)|0,o=o+Math.imul(m,tt)|0;var Mt=(u+(i=i+Math.imul(l,rt)|0)|0)+((8191&(n=(n=n+Math.imul(l,it)|0)+Math.imul(d,rt)|0))<<13)|0;u=((o=o+Math.imul(d,it)|0)+(n>>>13)|0)+(Mt>>>26)|0,Mt&=67108863,i=Math.imul(T,q),n=(n=Math.imul(T,F))+Math.imul(x,q)|0,o=Math.imul(x,F),i=i+Math.imul(k,H)|0,n=(n=n+Math.imul(k,K)|0)+Math.imul(B,H)|0,o=o+Math.imul(B,K)|0,i=i+Math.imul(A,G)|0,n=(n=n+Math.imul(A,Y)|0)+Math.imul(E,G)|0,o=o+Math.imul(E,Y)|0,i=i+Math.imul(w,$)|0,n=(n=n+Math.imul(w,X)|0)+Math.imul(M,$)|0,o=o+Math.imul(M,X)|0,i=i+Math.imul(v,Q)|0,n=(n=n+Math.imul(v,tt)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,tt)|0,i=i+Math.imul(p,rt)|0,n=(n=n+Math.imul(p,it)|0)+Math.imul(m,rt)|0,o=o+Math.imul(m,it)|0;var _t=(u+(i=i+Math.imul(l,ot)|0)|0)+((8191&(n=(n=n+Math.imul(l,st)|0)+Math.imul(d,ot)|0))<<13)|0;u=((o=o+Math.imul(d,st)|0)+(n>>>13)|0)+(_t>>>26)|0,_t&=67108863,i=Math.imul(C,q),n=(n=Math.imul(C,F))+Math.imul(P,q)|0,o=Math.imul(P,F),i=i+Math.imul(T,H)|0,n=(n=n+Math.imul(T,K)|0)+Math.imul(x,H)|0,o=o+Math.imul(x,K)|0,i=i+Math.imul(k,G)|0,n=(n=n+Math.imul(k,Y)|0)+Math.imul(B,G)|0,o=o+Math.imul(B,Y)|0,i=i+Math.imul(A,$)|0,n=(n=n+Math.imul(A,X)|0)+Math.imul(E,$)|0,o=o+Math.imul(E,X)|0,i=i+Math.imul(w,Q)|0,n=(n=n+Math.imul(w,tt)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,tt)|0,i=i+Math.imul(v,rt)|0,n=(n=n+Math.imul(v,it)|0)+Math.imul(b,rt)|0,o=o+Math.imul(b,it)|0,i=i+Math.imul(p,ot)|0,n=(n=n+Math.imul(p,st)|0)+Math.imul(m,ot)|0,o=o+Math.imul(m,st)|0;var At=(u+(i=i+Math.imul(l,ht)|0)|0)+((8191&(n=(n=n+Math.imul(l,ut)|0)+Math.imul(d,ht)|0))<<13)|0;u=((o=o+Math.imul(d,ut)|0)+(n>>>13)|0)+(At>>>26)|0,At&=67108863,i=Math.imul(L,q),n=(n=Math.imul(L,F))+Math.imul(O,q)|0,o=Math.imul(O,F),i=i+Math.imul(C,H)|0,n=(n=n+Math.imul(C,K)|0)+Math.imul(P,H)|0,o=o+Math.imul(P,K)|0,i=i+Math.imul(T,G)|0,n=(n=n+Math.imul(T,Y)|0)+Math.imul(x,G)|0,o=o+Math.imul(x,Y)|0,i=i+Math.imul(k,$)|0,n=(n=n+Math.imul(k,X)|0)+Math.imul(B,$)|0,o=o+Math.imul(B,X)|0,i=i+Math.imul(A,Q)|0,n=(n=n+Math.imul(A,tt)|0)+Math.imul(E,Q)|0,o=o+Math.imul(E,tt)|0,i=i+Math.imul(w,rt)|0,n=(n=n+Math.imul(w,it)|0)+Math.imul(M,rt)|0,o=o+Math.imul(M,it)|0,i=i+Math.imul(v,ot)|0,n=(n=n+Math.imul(v,st)|0)+Math.imul(b,ot)|0,o=o+Math.imul(b,st)|0,i=i+Math.imul(p,ht)|0,n=(n=n+Math.imul(p,ut)|0)+Math.imul(m,ht)|0,o=o+Math.imul(m,ut)|0;var Et=(u+(i=i+Math.imul(l,lt)|0)|0)+((8191&(n=(n=n+Math.imul(l,dt)|0)+Math.imul(d,lt)|0))<<13)|0;u=((o=o+Math.imul(d,dt)|0)+(n>>>13)|0)+(Et>>>26)|0,Et&=67108863,i=Math.imul(U,q),n=(n=Math.imul(U,F))+Math.imul(N,q)|0,o=Math.imul(N,F),i=i+Math.imul(L,H)|0,n=(n=n+Math.imul(L,K)|0)+Math.imul(O,H)|0,o=o+Math.imul(O,K)|0,i=i+Math.imul(C,G)|0,n=(n=n+Math.imul(C,Y)|0)+Math.imul(P,G)|0,o=o+Math.imul(P,Y)|0,i=i+Math.imul(T,$)|0,n=(n=n+Math.imul(T,X)|0)+Math.imul(x,$)|0,o=o+Math.imul(x,X)|0,i=i+Math.imul(k,Q)|0,n=(n=n+Math.imul(k,tt)|0)+Math.imul(B,Q)|0,o=o+Math.imul(B,tt)|0,i=i+Math.imul(A,rt)|0,n=(n=n+Math.imul(A,it)|0)+Math.imul(E,rt)|0,o=o+Math.imul(E,it)|0,i=i+Math.imul(w,ot)|0,n=(n=n+Math.imul(w,st)|0)+Math.imul(M,ot)|0,o=o+Math.imul(M,st)|0,i=i+Math.imul(v,ht)|0,n=(n=n+Math.imul(v,ut)|0)+Math.imul(b,ht)|0,o=o+Math.imul(b,ut)|0,i=i+Math.imul(p,lt)|0,n=(n=n+Math.imul(p,dt)|0)+Math.imul(m,lt)|0,o=o+Math.imul(m,dt)|0;var St=(u+(i=i+Math.imul(l,pt)|0)|0)+((8191&(n=(n=n+Math.imul(l,mt)|0)+Math.imul(d,pt)|0))<<13)|0;u=((o=o+Math.imul(d,mt)|0)+(n>>>13)|0)+(St>>>26)|0,St&=67108863,i=Math.imul(U,H),n=(n=Math.imul(U,K))+Math.imul(N,H)|0,o=Math.imul(N,K),i=i+Math.imul(L,G)|0,n=(n=n+Math.imul(L,Y)|0)+Math.imul(O,G)|0,o=o+Math.imul(O,Y)|0,i=i+Math.imul(C,$)|0,n=(n=n+Math.imul(C,X)|0)+Math.imul(P,$)|0,o=o+Math.imul(P,X)|0,i=i+Math.imul(T,Q)|0,n=(n=n+Math.imul(T,tt)|0)+Math.imul(x,Q)|0,o=o+Math.imul(x,tt)|0,i=i+Math.imul(k,rt)|0,n=(n=n+Math.imul(k,it)|0)+Math.imul(B,rt)|0,o=o+Math.imul(B,it)|0,i=i+Math.imul(A,ot)|0,n=(n=n+Math.imul(A,st)|0)+Math.imul(E,ot)|0,o=o+Math.imul(E,st)|0,i=i+Math.imul(w,ht)|0,n=(n=n+Math.imul(w,ut)|0)+Math.imul(M,ht)|0,o=o+Math.imul(M,ut)|0,i=i+Math.imul(v,lt)|0,n=(n=n+Math.imul(v,dt)|0)+Math.imul(b,lt)|0,o=o+Math.imul(b,dt)|0;var kt=(u+(i=i+Math.imul(p,pt)|0)|0)+((8191&(n=(n=n+Math.imul(p,mt)|0)+Math.imul(m,pt)|0))<<13)|0;u=((o=o+Math.imul(m,mt)|0)+(n>>>13)|0)+(kt>>>26)|0,kt&=67108863,i=Math.imul(U,G),n=(n=Math.imul(U,Y))+Math.imul(N,G)|0,o=Math.imul(N,Y),i=i+Math.imul(L,$)|0,n=(n=n+Math.imul(L,X)|0)+Math.imul(O,$)|0,o=o+Math.imul(O,X)|0,i=i+Math.imul(C,Q)|0,n=(n=n+Math.imul(C,tt)|0)+Math.imul(P,Q)|0,o=o+Math.imul(P,tt)|0,i=i+Math.imul(T,rt)|0,n=(n=n+Math.imul(T,it)|0)+Math.imul(x,rt)|0,o=o+Math.imul(x,it)|0,i=i+Math.imul(k,ot)|0,n=(n=n+Math.imul(k,st)|0)+Math.imul(B,ot)|0,o=o+Math.imul(B,st)|0,i=i+Math.imul(A,ht)|0,n=(n=n+Math.imul(A,ut)|0)+Math.imul(E,ht)|0,o=o+Math.imul(E,ut)|0,i=i+Math.imul(w,lt)|0,n=(n=n+Math.imul(w,dt)|0)+Math.imul(M,lt)|0,o=o+Math.imul(M,dt)|0;var Bt=(u+(i=i+Math.imul(v,pt)|0)|0)+((8191&(n=(n=n+Math.imul(v,mt)|0)+Math.imul(b,pt)|0))<<13)|0;u=((o=o+Math.imul(b,mt)|0)+(n>>>13)|0)+(Bt>>>26)|0,Bt&=67108863,i=Math.imul(U,$),n=(n=Math.imul(U,X))+Math.imul(N,$)|0,o=Math.imul(N,X),i=i+Math.imul(L,Q)|0,n=(n=n+Math.imul(L,tt)|0)+Math.imul(O,Q)|0,o=o+Math.imul(O,tt)|0,i=i+Math.imul(C,rt)|0,n=(n=n+Math.imul(C,it)|0)+Math.imul(P,rt)|0,o=o+Math.imul(P,it)|0,i=i+Math.imul(T,ot)|0,n=(n=n+Math.imul(T,st)|0)+Math.imul(x,ot)|0,o=o+Math.imul(x,st)|0,i=i+Math.imul(k,ht)|0,n=(n=n+Math.imul(k,ut)|0)+Math.imul(B,ht)|0,o=o+Math.imul(B,ut)|0,i=i+Math.imul(A,lt)|0,n=(n=n+Math.imul(A,dt)|0)+Math.imul(E,lt)|0,o=o+Math.imul(E,dt)|0;var Rt=(u+(i=i+Math.imul(w,pt)|0)|0)+((8191&(n=(n=n+Math.imul(w,mt)|0)+Math.imul(M,pt)|0))<<13)|0;u=((o=o+Math.imul(M,mt)|0)+(n>>>13)|0)+(Rt>>>26)|0,Rt&=67108863,i=Math.imul(U,Q),n=(n=Math.imul(U,tt))+Math.imul(N,Q)|0,o=Math.imul(N,tt),i=i+Math.imul(L,rt)|0,n=(n=n+Math.imul(L,it)|0)+Math.imul(O,rt)|0,o=o+Math.imul(O,it)|0,i=i+Math.imul(C,ot)|0,n=(n=n+Math.imul(C,st)|0)+Math.imul(P,ot)|0,o=o+Math.imul(P,st)|0,i=i+Math.imul(T,ht)|0,n=(n=n+Math.imul(T,ut)|0)+Math.imul(x,ht)|0,o=o+Math.imul(x,ut)|0,i=i+Math.imul(k,lt)|0,n=(n=n+Math.imul(k,dt)|0)+Math.imul(B,lt)|0,o=o+Math.imul(B,dt)|0;var Tt=(u+(i=i+Math.imul(A,pt)|0)|0)+((8191&(n=(n=n+Math.imul(A,mt)|0)+Math.imul(E,pt)|0))<<13)|0;u=((o=o+Math.imul(E,mt)|0)+(n>>>13)|0)+(Tt>>>26)|0,Tt&=67108863,i=Math.imul(U,rt),n=(n=Math.imul(U,it))+Math.imul(N,rt)|0,o=Math.imul(N,it),i=i+Math.imul(L,ot)|0,n=(n=n+Math.imul(L,st)|0)+Math.imul(O,ot)|0,o=o+Math.imul(O,st)|0,i=i+Math.imul(C,ht)|0,n=(n=n+Math.imul(C,ut)|0)+Math.imul(P,ht)|0,o=o+Math.imul(P,ut)|0,i=i+Math.imul(T,lt)|0,n=(n=n+Math.imul(T,dt)|0)+Math.imul(x,lt)|0,o=o+Math.imul(x,dt)|0;var xt=(u+(i=i+Math.imul(k,pt)|0)|0)+((8191&(n=(n=n+Math.imul(k,mt)|0)+Math.imul(B,pt)|0))<<13)|0;u=((o=o+Math.imul(B,mt)|0)+(n>>>13)|0)+(xt>>>26)|0,xt&=67108863,i=Math.imul(U,ot),n=(n=Math.imul(U,st))+Math.imul(N,ot)|0,o=Math.imul(N,st),i=i+Math.imul(L,ht)|0,n=(n=n+Math.imul(L,ut)|0)+Math.imul(O,ht)|0,o=o+Math.imul(O,ut)|0,i=i+Math.imul(C,lt)|0,n=(n=n+Math.imul(C,dt)|0)+Math.imul(P,lt)|0,o=o+Math.imul(P,dt)|0;var It=(u+(i=i+Math.imul(T,pt)|0)|0)+((8191&(n=(n=n+Math.imul(T,mt)|0)+Math.imul(x,pt)|0))<<13)|0;u=((o=o+Math.imul(x,mt)|0)+(n>>>13)|0)+(It>>>26)|0,It&=67108863,i=Math.imul(U,ht),n=(n=Math.imul(U,ut))+Math.imul(N,ht)|0,o=Math.imul(N,ut),i=i+Math.imul(L,lt)|0,n=(n=n+Math.imul(L,dt)|0)+Math.imul(O,lt)|0,o=o+Math.imul(O,dt)|0;var Ct=(u+(i=i+Math.imul(C,pt)|0)|0)+((8191&(n=(n=n+Math.imul(C,mt)|0)+Math.imul(P,pt)|0))<<13)|0;u=((o=o+Math.imul(P,mt)|0)+(n>>>13)|0)+(Ct>>>26)|0,Ct&=67108863,i=Math.imul(U,lt),n=(n=Math.imul(U,dt))+Math.imul(N,lt)|0,o=Math.imul(N,dt);var Pt=(u+(i=i+Math.imul(L,pt)|0)|0)+((8191&(n=(n=n+Math.imul(L,mt)|0)+Math.imul(O,pt)|0))<<13)|0;u=((o=o+Math.imul(O,mt)|0)+(n>>>13)|0)+(Pt>>>26)|0,Pt&=67108863;var Dt=(u+(i=Math.imul(U,pt))|0)+((8191&(n=(n=Math.imul(U,mt))+Math.imul(N,pt)|0))<<13)|0;return u=((o=Math.imul(N,mt))+(n>>>13)|0)+(Dt>>>26)|0,Dt&=67108863,h[0]=gt,h[1]=vt,h[2]=bt,h[3]=yt,h[4]=wt,h[5]=Mt,h[6]=_t,h[7]=At,h[8]=Et,h[9]=St,h[10]=kt,h[11]=Bt,h[12]=Rt,h[13]=Tt,h[14]=xt,h[15]=It,h[16]=Ct,h[17]=Pt,h[18]=Dt,0!==u&&(h[19]=u,r.length++),r};function p(t,e,r){return(new m).mulp(t,e,r)}function m(t,e){this.x=t,this.y=e}Math.imul||(c=d),n.prototype.mulTo=function(t,e){var r,i=this.length+t.length;return r=10===this.length&&10===t.length?c(this,t,e):i<63?d(this,t,e):i<1024?function(t,e,r){r.negative=e.negative^t.negative,r.length=t.length+e.length;for(var i=0,n=0,o=0;o>>26)|0)>>>26,s&=67108863}r.words[o]=a,i=s,s=n}return 0!==i?r.words[o]=i:r.length--,r.strip()}(this,t,e):p(this,t,e),r},m.prototype.makeRBT=function(t){for(var e=new Array(t),r=n.prototype._countBits(t)-1,i=0;i>=1;return i},m.prototype.permute=function(t,e,r,i,n,o){for(var s=0;s>>=1)n++;return 1<>>=13,i[2*s+1]=8191&o,o>>>=13;for(s=2*e;s>=26,e+=n/67108864|0,e+=o>>>26,this.words[i]=67108863&o}return 0!==e&&(this.words[i]=e,this.length++),this},n.prototype.muln=function(t){return this.clone().imuln(t)},n.prototype.sqr=function(){return this.mul(this)},n.prototype.isqr=function(){return this.imul(this.clone())},n.prototype.pow=function(t){var e=function(t){for(var e=new Array(t.bitLength()),r=0;r>>n}return e}(t);if(0===e.length)return new n(1);for(var r=this,i=0;i=0);var e,i=t%26,n=(t-i)/26,o=67108863>>>26-i<<26-i;if(0!==i){var s=0;for(e=0;e>>26-i}s&&(this.words[e]=s,this.length++)}if(0!==n){for(e=this.length-1;e>=0;e--)this.words[e+n]=this.words[e];for(e=0;e=0),n=e?(e-e%26)/26:0;var o=t%26,s=Math.min((t-o)/26,this.length),a=67108863^67108863>>>o<s)for(this.length-=s,u=0;u=0&&(0!==f||u>=n);u--){var l=0|this.words[u];this.words[u]=f<<26-o|l>>>o,f=l&a}return h&&0!==f&&(h.words[h.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},n.prototype.ishrn=function(t,e,i){return r(0===this.negative),this.iushrn(t,e,i)},n.prototype.shln=function(t){return this.clone().ishln(t)},n.prototype.ushln=function(t){return this.clone().iushln(t)},n.prototype.shrn=function(t){return this.clone().ishrn(t)},n.prototype.ushrn=function(t){return this.clone().iushrn(t)},n.prototype.testn=function(t){r("number"==typeof t&&t>=0);var e=t%26,i=(t-e)/26,n=1<=0);var e=t%26,i=(t-e)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=i)return this;if(0!==e&&i++,this.length=Math.min(i,this.length),0!==e){var n=67108863^67108863>>>e<=67108864;e++)this.words[e]-=67108864,e===this.length-1?this.words[e+1]=1:this.words[e+1]++;return this.length=Math.max(this.length,e+1),this},n.prototype.isubn=function(t){if(r("number"==typeof t),r(t<67108864),t<0)return this.iaddn(-t);if(0!==this.negative)return this.negative=0,this.iaddn(t),this.negative=1,this;if(this.words[0]-=t,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var e=0;e>26)-(h/67108864|0),this.words[n+i]=67108863&o}for(;n>26,this.words[n+i]=67108863&o;if(0===a)return this.strip();for(r(-1===a),a=0,n=0;n>26,this.words[n]=67108863&o;return this.negative=1,this.strip()},n.prototype._wordDiv=function(t,e){var r=(this.length,t.length),i=this.clone(),o=t,s=0|o.words[o.length-1];0!==(r=26-this._countBits(s))&&(o=o.ushln(r),i.iushln(r),s=0|o.words[o.length-1]);var a,h=i.length-o.length;if("mod"!==e){(a=new n(null)).length=h+1,a.words=new Array(a.length);for(var u=0;u=0;l--){var d=67108864*(0|i.words[o.length+l])+(0|i.words[o.length+l-1]);for(d=Math.min(d/s|0,67108863),i._ishlnsubmul(o,d,l);0!==i.negative;)d--,i.negative=0,i._ishlnsubmul(o,1,l),i.isZero()||(i.negative^=1);a&&(a.words[l]=d)}return a&&a.strip(),i.strip(),"div"!==e&&0!==r&&i.iushrn(r),{div:a||null,mod:i}},n.prototype.divmod=function(t,e,i){return r(!t.isZero()),this.isZero()?{div:new n(0),mod:new n(0)}:0!==this.negative&&0===t.negative?(a=this.neg().divmod(t,e),"mod"!==e&&(o=a.div.neg()),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.iadd(t)),{div:o,mod:s}):0===this.negative&&0!==t.negative?(a=this.divmod(t.neg(),e),"mod"!==e&&(o=a.div.neg()),{div:o,mod:a.mod}):0!=(this.negative&t.negative)?(a=this.neg().divmod(t.neg(),e),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.isub(t)),{div:a.div,mod:s}):t.length>this.length||this.cmp(t)<0?{div:new n(0),mod:this}:1===t.length?"div"===e?{div:this.divn(t.words[0]),mod:null}:"mod"===e?{div:null,mod:new n(this.modn(t.words[0]))}:{div:this.divn(t.words[0]),mod:new n(this.modn(t.words[0]))}:this._wordDiv(t,e);var o,s,a},n.prototype.div=function(t){return this.divmod(t,"div",!1).div},n.prototype.mod=function(t){return this.divmod(t,"mod",!1).mod},n.prototype.umod=function(t){return this.divmod(t,"mod",!0).mod},n.prototype.divRound=function(t){var e=this.divmod(t);if(e.mod.isZero())return e.div;var r=0!==e.div.negative?e.mod.isub(t):e.mod,i=t.ushrn(1),n=t.andln(1),o=r.cmp(i);return o<0||1===n&&0===o?e.div:0!==e.div.negative?e.div.isubn(1):e.div.iaddn(1)},n.prototype.modn=function(t){r(t<=67108863);for(var e=(1<<26)%t,i=0,n=this.length-1;n>=0;n--)i=(e*i+(0|this.words[n]))%t;return i},n.prototype.idivn=function(t){r(t<=67108863);for(var e=0,i=this.length-1;i>=0;i--){var n=(0|this.words[i])+67108864*e;this.words[i]=n/t|0,e=n%t}return this.strip()},n.prototype.divn=function(t){return this.clone().idivn(t)},n.prototype.egcd=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o=new n(1),s=new n(0),a=new n(0),h=new n(1),u=0;e.isEven()&&i.isEven();)e.iushrn(1),i.iushrn(1),++u;for(var f=i.clone(),l=e.clone();!e.isZero();){for(var d=0,c=1;0==(e.words[0]&c)&&d<26;++d,c<<=1);if(d>0)for(e.iushrn(d);d-- >0;)(o.isOdd()||s.isOdd())&&(o.iadd(f),s.isub(l)),o.iushrn(1),s.iushrn(1);for(var p=0,m=1;0==(i.words[0]&m)&&p<26;++p,m<<=1);if(p>0)for(i.iushrn(p);p-- >0;)(a.isOdd()||h.isOdd())&&(a.iadd(f),h.isub(l)),a.iushrn(1),h.iushrn(1);e.cmp(i)>=0?(e.isub(i),o.isub(a),s.isub(h)):(i.isub(e),a.isub(o),h.isub(s))}return{a:a,b:h,gcd:i.iushln(u)}},n.prototype._invmp=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o,s=new n(1),a=new n(0),h=i.clone();e.cmpn(1)>0&&i.cmpn(1)>0;){for(var u=0,f=1;0==(e.words[0]&f)&&u<26;++u,f<<=1);if(u>0)for(e.iushrn(u);u-- >0;)s.isOdd()&&s.iadd(h),s.iushrn(1);for(var l=0,d=1;0==(i.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(i.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(h),a.iushrn(1);e.cmp(i)>=0?(e.isub(i),s.isub(a)):(i.isub(e),a.isub(s))}return(o=0===e.cmpn(1)?s:a).cmpn(0)<0&&o.iadd(t),o},n.prototype.gcd=function(t){if(this.isZero())return t.abs();if(t.isZero())return this.abs();var e=this.clone(),r=t.clone();e.negative=0,r.negative=0;for(var i=0;e.isEven()&&r.isEven();i++)e.iushrn(1),r.iushrn(1);for(;;){for(;e.isEven();)e.iushrn(1);for(;r.isEven();)r.iushrn(1);var n=e.cmp(r);if(n<0){var o=e;e=r,r=o}else if(0===n||0===r.cmpn(1))break;e.isub(r)}return r.iushln(i)},n.prototype.invm=function(t){return this.egcd(t).a.umod(t)},n.prototype.isEven=function(){return 0==(1&this.words[0])},n.prototype.isOdd=function(){return 1==(1&this.words[0])},n.prototype.andln=function(t){return this.words[0]&t},n.prototype.bincn=function(t){r("number"==typeof t);var e=t%26,i=(t-e)/26,n=1<>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},n.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},n.prototype.cmpn=function(t){var e,i=t<0;if(0!==this.negative&&!i)return-1;if(0===this.negative&&i)return 1;if(this.strip(),this.length>1)e=1;else{i&&(t=-t),r(t<=67108863,"Number is too big");var n=0|this.words[0];e=n===t?0:nt.length)return 1;if(this.length=0;r--){var i=0|this.words[r],n=0|t.words[r];if(i!==n){in&&(e=1);break}}return e},n.prototype.gtn=function(t){return 1===this.cmpn(t)},n.prototype.gt=function(t){return 1===this.cmp(t)},n.prototype.gten=function(t){return this.cmpn(t)>=0},n.prototype.gte=function(t){return this.cmp(t)>=0},n.prototype.ltn=function(t){return-1===this.cmpn(t)},n.prototype.lt=function(t){return-1===this.cmp(t)},n.prototype.lten=function(t){return this.cmpn(t)<=0},n.prototype.lte=function(t){return this.cmp(t)<=0},n.prototype.eqn=function(t){return 0===this.cmpn(t)},n.prototype.eq=function(t){return 0===this.cmp(t)},n.red=function(t){return new _(t)},n.prototype.toRed=function(t){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),t.convertTo(this)._forceRed(t)},n.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},n.prototype._forceRed=function(t){return this.red=t,this},n.prototype.forceRed=function(t){return r(!this.red,"Already a number in reduction context"),this._forceRed(t)},n.prototype.redAdd=function(t){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,t)},n.prototype.redIAdd=function(t){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,t)},n.prototype.redSub=function(t){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,t)},n.prototype.redISub=function(t){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,t)},n.prototype.redShl=function(t){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,t)},n.prototype.redMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.mul(this,t)},n.prototype.redIMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.imul(this,t)},n.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},n.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},n.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},n.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},n.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},n.prototype.redPow=function(t){return r(this.red&&!t.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,t)};var g={k256:null,p224:null,p192:null,p25519:null};function v(t,e){this.name=t,this.p=new n(e,16),this.n=this.p.bitLength(),this.k=new n(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function b(){v.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function y(){v.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function w(){v.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function M(){v.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function _(t){if("string"==typeof t){var e=n._prime(t);this.m=e.p,this.prime=e}else r(t.gtn(1),"modulus must be greater than 1"),this.m=t,this.prime=null}function A(t){_.call(this,t),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new n(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}v.prototype._tmp=function(){var t=new n(null);return t.words=new Array(Math.ceil(this.n/13)),t},v.prototype.ireduce=function(t){var e,r=t;do{this.split(r,this.tmp),e=(r=(r=this.imulK(r)).iadd(this.tmp)).bitLength()}while(e>this.n);var i=e0?r.isub(this.p):void 0!==r.strip?r.strip():r._strip(),r},v.prototype.split=function(t,e){t.iushrn(this.n,0,e)},v.prototype.imulK=function(t){return t.imul(this.k)},i(b,v),b.prototype.split=function(t,e){for(var r=4194303,i=Math.min(t.length,9),n=0;n>>22,o=s}o>>>=22,t.words[n-10]=o,0===o&&t.length>10?t.length-=10:t.length-=9},b.prototype.imulK=function(t){t.words[t.length]=0,t.words[t.length+1]=0,t.length+=2;for(var e=0,r=0;r>>=26,t.words[r]=n,e=i}return 0!==e&&(t.words[t.length++]=e),t},n._prime=function(t){if(g[t])return g[t];var e;if("k256"===t)e=new b;else if("p224"===t)e=new y;else if("p192"===t)e=new w;else{if("p25519"!==t)throw new Error("Unknown prime "+t);e=new M}return g[t]=e,e},_.prototype._verify1=function(t){r(0===t.negative,"red works only with positives"),r(t.red,"red works only with red numbers")},_.prototype._verify2=function(t,e){r(0==(t.negative|e.negative),"red works only with positives"),r(t.red&&t.red===e.red,"red works only with red numbers")},_.prototype.imod=function(t){return this.prime?this.prime.ireduce(t)._forceRed(this):t.umod(this.m)._forceRed(this)},_.prototype.neg=function(t){return t.isZero()?t.clone():this.m.sub(t)._forceRed(this)},_.prototype.add=function(t,e){this._verify2(t,e);var r=t.add(e);return r.cmp(this.m)>=0&&r.isub(this.m),r._forceRed(this)},_.prototype.iadd=function(t,e){this._verify2(t,e);var r=t.iadd(e);return r.cmp(this.m)>=0&&r.isub(this.m),r},_.prototype.sub=function(t,e){this._verify2(t,e);var r=t.sub(e);return r.cmpn(0)<0&&r.iadd(this.m),r._forceRed(this)},_.prototype.isub=function(t,e){this._verify2(t,e);var r=t.isub(e);return r.cmpn(0)<0&&r.iadd(this.m),r},_.prototype.shl=function(t,e){return this._verify1(t),this.imod(t.ushln(e))},_.prototype.imul=function(t,e){return this._verify2(t,e),this.imod(t.imul(e))},_.prototype.mul=function(t,e){return this._verify2(t,e),this.imod(t.mul(e))},_.prototype.isqr=function(t){return this.imul(t,t.clone())},_.prototype.sqr=function(t){return this.mul(t,t)},_.prototype.sqrt=function(t){if(t.isZero())return t.clone();var e=this.m.andln(3);if(r(e%2==1),3===e){var i=this.m.add(new n(1)).iushrn(2);return this.pow(t,i)}for(var o=this.m.subn(1),s=0;!o.isZero()&&0===o.andln(1);)s++,o.iushrn(1);r(!o.isZero());var a=new n(1).toRed(this),h=a.redNeg(),u=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new n(2*f*f).toRed(this);0!==this.pow(f,u).cmp(h);)f.redIAdd(h);for(var l=this.pow(f,o),d=this.pow(t,o.addn(1).iushrn(1)),c=this.pow(t,o),p=s;0!==c.cmp(a);){for(var m=c,g=0;0!==m.cmp(a);g++)m=m.redSqr();r(g=0;i--){for(var u=e.words[i],f=h-1;f>=0;f--){var l=u>>f&1;o!==r[0]&&(o=this.sqr(o)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===i&&0===f)&&(o=this.mul(o,r[s]),a=0,s=0)):a=0}h=26}return o},_.prototype.convertTo=function(t){var e=t.umod(this.m);return e===t?e.clone():e},_.prototype.convertFrom=function(t){var e=t.clone();return e.red=null,e},n.mont=function(t){return new A(t)},i(A,_),A.prototype.convertTo=function(t){return this.imod(t.ushln(this.shift))},A.prototype.convertFrom=function(t){var e=this.imod(t.mul(this.rinv));return e.red=null,e},A.prototype.imul=function(t,e){if(t.isZero()||e.isZero())return t.words[0]=0,t.length=1,t;var r=t.imul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),n=r.isub(i).iushrn(this.shift),o=n;return n.cmp(this.m)>=0?o=n.isub(this.m):n.cmpn(0)<0&&(o=n.iadd(this.m)),o._forceRed(this)},A.prototype.mul=function(t,e){if(t.isZero()||e.isZero())return new n(0)._forceRed(this);var r=t.mul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),o=r.isub(i).iushrn(this.shift),s=o;return o.cmp(this.m)>=0?s=o.isub(this.m):o.cmpn(0)<0&&(s=o.iadd(this.m)),s._forceRed(this)},A.prototype.invm=function(t){return this.imod(t._invmp(this.m).mul(this.r2))._forceRed(this)}}(void 0===t||t,this),t.exports}.call({})}function pm(){return dm||(dm=!0,cm()),lm}var mm,gm=!1;function vm(){mm=function(){var t={exports:this};return function(t,e){function r(t,e){if(!t)throw new Error(e||"Assertion failed")}function i(t,e){t.super_=e;var r=function(){};r.prototype=e.prototype,t.prototype=new r,t.prototype.constructor=t}function n(t,e,r){if(n.isBN(t))return t;this.negative=0,this.words=null,this.length=0,this.red=null,null!==t&&("le"!==e&&"be"!==e||(r=e,e=10),this._init(t||0,e||10,r||"be"))}var o;"object"==typeof t?t.exports=n:e.BN=n,n.BN=n,n.wordSize=26;try{o="undefined"!=typeof window&&void 0!==window.Buffer?window.Buffer:ir().Buffer}catch(t){}function s(t,e){var r=t.charCodeAt(e);return r>=65&&r<=70?r-55:r>=97&&r<=102?r-87:r-48&15}function a(t,e,r){var i=s(t,r);return r-1>=e&&(i|=s(t,r-1)<<4),i}function h(t,e,r,i){for(var n=0,o=Math.min(t.length,r),s=e;s=49?a-49+10:a>=17?a-17+10:a}return n}n.isBN=function(t){return t instanceof n||null!==t&&"object"==typeof t&&t.constructor.wordSize===n.wordSize&&Array.isArray(t.words)},n.max=function(t,e){return t.cmp(e)>0?t:e},n.min=function(t,e){return t.cmp(e)<0?t:e},n.prototype._init=function(t,e,i){if("number"==typeof t)return this._initNumber(t,e,i);if("object"==typeof t)return this._initArray(t,e,i);"hex"===e&&(e=16),r(e===(0|e)&&e>=2&&e<=36);var n=0;"-"===(t=t.toString().replace(/\s+/g,""))[0]&&(n++,this.negative=1),n=0;n-=3)s=t[n]|t[n-1]<<8|t[n-2]<<16,this.words[o]|=s<>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===i)for(n=0,o=0;n>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this.strip()},n.prototype._parseHex=function(t,e,r){this.length=Math.ceil((t.length-e)/6),this.words=new Array(this.length);for(var i=0;i=e;i-=2)n=a(t,e,i)<=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;else for(i=(t.length-e)%2==0?e+1:e;i=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;this.strip()},n.prototype._parseBase=function(t,e,r){this.words=[0],this.length=1;for(var i=0,n=1;n<=67108863;n*=e)i++;i--,n=n/e|0;for(var o=t.length-r,s=o%i,a=Math.min(o,o-s)+r,u=0,f=r;f1&&0===this.words[this.length-1];)this.length--;return this._normSign()},n.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},n.prototype.inspect=function(){return(this.red?""};var u=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],f=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],l=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];function d(t,e,r){r.negative=e.negative^t.negative;var i=t.length+e.length|0;r.length=i,i=i-1|0;var n=0|t.words[0],o=0|e.words[0],s=n*o,a=67108863&s,h=s/67108864|0;r.words[0]=a;for(var u=1;u>>26,l=67108863&h,d=Math.min(u,e.length-1),c=Math.max(0,u-t.length+1);c<=d;c++){var p=u-c|0;f+=(s=(n=0|t.words[p])*(o=0|e.words[c])+l)/67108864|0,l=67108863&s}r.words[u]=0|l,h=0|f}return 0!==h?r.words[u]=0|h:r.length--,r.strip()}n.prototype.toString=function(t,e){var i;if(e=0|e||1,16===(t=t||10)||"hex"===t){i="";for(var n=0,o=0,s=0;s>>24-n&16777215)||s!==this.length-1?u[6-h.length]+h+i:h+i,(n+=2)>=26&&(n-=26,s--)}for(0!==o&&(i=o.toString(16)+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}if(t===(0|t)&&t>=2&&t<=36){var d=f[t],c=l[t];i="";var p=this.clone();for(p.negative=0;!p.isZero();){var m=p.modn(c).toString(t);i=(p=p.idivn(c)).isZero()?m+i:u[d-m.length]+m+i}for(this.isZero()&&(i="0"+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}r(!1,"Base should be between 2 and 36")},n.prototype.toNumber=function(){var t=this.words[0];return 2===this.length?t+=67108864*this.words[1]:3===this.length&&1===this.words[2]?t+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-t:t},n.prototype.toJSON=function(){return this.toString(16)},n.prototype.toBuffer=function(t,e){return r(void 0!==o),this.toArrayLike(o,t,e)},n.prototype.toArray=function(t,e){return this.toArrayLike(Array,t,e)},n.prototype.toArrayLike=function(t,e,i){var n=this.byteLength(),o=i||Math.max(1,n);r(n<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0"),this.strip();var s,a,h="le"===e,u=new t(o),f=this.clone();if(h){for(a=0;!f.isZero();a++)s=f.andln(255),f.iushrn(8),u[a]=s;for(;a=4096&&(r+=13,e>>>=13),e>=64&&(r+=7,e>>>=7),e>=8&&(r+=4,e>>>=4),e>=2&&(r+=2,e>>>=2),r+e},n.prototype._zeroBits=function(t){if(0===t)return 26;var e=t,r=0;return 0==(8191&e)&&(r+=13,e>>>=13),0==(127&e)&&(r+=7,e>>>=7),0==(15&e)&&(r+=4,e>>>=4),0==(3&e)&&(r+=2,e>>>=2),0==(1&e)&&r++,r},n.prototype.bitLength=function(){var t=this.words[this.length-1],e=this._countBits(t);return 26*(this.length-1)+e},n.prototype.zeroBits=function(){if(this.isZero())return 0;for(var t=0,e=0;et.length?this.clone().ior(t):t.clone().ior(this)},n.prototype.uor=function(t){return this.length>t.length?this.clone().iuor(t):t.clone().iuor(this)},n.prototype.iuand=function(t){var e;e=this.length>t.length?t:this;for(var r=0;rt.length?this.clone().iand(t):t.clone().iand(this)},n.prototype.uand=function(t){return this.length>t.length?this.clone().iuand(t):t.clone().iuand(this)},n.prototype.iuxor=function(t){var e,r;this.length>t.length?(e=this,r=t):(e=t,r=this);for(var i=0;it.length?this.clone().ixor(t):t.clone().ixor(this)},n.prototype.uxor=function(t){return this.length>t.length?this.clone().iuxor(t):t.clone().iuxor(this)},n.prototype.inotn=function(t){r("number"==typeof t&&t>=0);var e=0|Math.ceil(t/26),i=t%26;this._expand(e),i>0&&e--;for(var n=0;n0&&(this.words[n]=~this.words[n]&67108863>>26-i),this.strip()},n.prototype.notn=function(t){return this.clone().inotn(t)},n.prototype.setn=function(t,e){r("number"==typeof t&&t>=0);var i=t/26|0,n=t%26;return this._expand(i+1),this.words[i]=e?this.words[i]|1<t.length?(r=this,i=t):(r=t,i=this);for(var n=0,o=0;o>>26;for(;0!==n&&o>>26;if(this.length=r.length,0!==n)this.words[this.length]=n,this.length++;else if(r!==this)for(;ot.length?this.clone().iadd(t):t.clone().iadd(this)},n.prototype.isub=function(t){if(0!==t.negative){t.negative=0;var e=this.iadd(t);return t.negative=1,e._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(t),this.negative=1,this._normSign();var r,i,n=this.cmp(t);if(0===n)return this.negative=0,this.length=1,this.words[0]=0,this;n>0?(r=this,i=t):(r=t,i=this);for(var o=0,s=0;s>26,this.words[s]=67108863&e;for(;0!==o&&s>26,this.words[s]=67108863&e;if(0===o&&s>>13,c=0|s[1],p=8191&c,m=c>>>13,g=0|s[2],v=8191&g,b=g>>>13,y=0|s[3],w=8191&y,M=y>>>13,_=0|s[4],A=8191&_,E=_>>>13,S=0|s[5],k=8191&S,B=S>>>13,R=0|s[6],T=8191&R,x=R>>>13,I=0|s[7],C=8191&I,P=I>>>13,D=0|s[8],L=8191&D,O=D>>>13,j=0|s[9],U=8191&j,N=j>>>13,z=0|a[0],q=8191&z,F=z>>>13,Z=0|a[1],H=8191&Z,K=Z>>>13,W=0|a[2],G=8191&W,Y=W>>>13,V=0|a[3],$=8191&V,X=V>>>13,J=0|a[4],Q=8191&J,tt=J>>>13,et=0|a[5],rt=8191&et,it=et>>>13,nt=0|a[6],ot=8191&nt,st=nt>>>13,at=0|a[7],ht=8191&at,ut=at>>>13,ft=0|a[8],lt=8191&ft,dt=ft>>>13,ct=0|a[9],pt=8191&ct,mt=ct>>>13;r.negative=t.negative^e.negative,r.length=19;var gt=(u+(i=Math.imul(l,q))|0)+((8191&(n=(n=Math.imul(l,F))+Math.imul(d,q)|0))<<13)|0;u=((o=Math.imul(d,F))+(n>>>13)|0)+(gt>>>26)|0,gt&=67108863,i=Math.imul(p,q),n=(n=Math.imul(p,F))+Math.imul(m,q)|0,o=Math.imul(m,F);var vt=(u+(i=i+Math.imul(l,H)|0)|0)+((8191&(n=(n=n+Math.imul(l,K)|0)+Math.imul(d,H)|0))<<13)|0;u=((o=o+Math.imul(d,K)|0)+(n>>>13)|0)+(vt>>>26)|0,vt&=67108863,i=Math.imul(v,q),n=(n=Math.imul(v,F))+Math.imul(b,q)|0,o=Math.imul(b,F),i=i+Math.imul(p,H)|0,n=(n=n+Math.imul(p,K)|0)+Math.imul(m,H)|0,o=o+Math.imul(m,K)|0;var bt=(u+(i=i+Math.imul(l,G)|0)|0)+((8191&(n=(n=n+Math.imul(l,Y)|0)+Math.imul(d,G)|0))<<13)|0;u=((o=o+Math.imul(d,Y)|0)+(n>>>13)|0)+(bt>>>26)|0,bt&=67108863,i=Math.imul(w,q),n=(n=Math.imul(w,F))+Math.imul(M,q)|0,o=Math.imul(M,F),i=i+Math.imul(v,H)|0,n=(n=n+Math.imul(v,K)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,K)|0,i=i+Math.imul(p,G)|0,n=(n=n+Math.imul(p,Y)|0)+Math.imul(m,G)|0,o=o+Math.imul(m,Y)|0;var yt=(u+(i=i+Math.imul(l,$)|0)|0)+((8191&(n=(n=n+Math.imul(l,X)|0)+Math.imul(d,$)|0))<<13)|0;u=((o=o+Math.imul(d,X)|0)+(n>>>13)|0)+(yt>>>26)|0,yt&=67108863,i=Math.imul(A,q),n=(n=Math.imul(A,F))+Math.imul(E,q)|0,o=Math.imul(E,F),i=i+Math.imul(w,H)|0,n=(n=n+Math.imul(w,K)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,K)|0,i=i+Math.imul(v,G)|0,n=(n=n+Math.imul(v,Y)|0)+Math.imul(b,G)|0,o=o+Math.imul(b,Y)|0,i=i+Math.imul(p,$)|0,n=(n=n+Math.imul(p,X)|0)+Math.imul(m,$)|0,o=o+Math.imul(m,X)|0;var wt=(u+(i=i+Math.imul(l,Q)|0)|0)+((8191&(n=(n=n+Math.imul(l,tt)|0)+Math.imul(d,Q)|0))<<13)|0;u=((o=o+Math.imul(d,tt)|0)+(n>>>13)|0)+(wt>>>26)|0,wt&=67108863,i=Math.imul(k,q),n=(n=Math.imul(k,F))+Math.imul(B,q)|0,o=Math.imul(B,F),i=i+Math.imul(A,H)|0,n=(n=n+Math.imul(A,K)|0)+Math.imul(E,H)|0,o=o+Math.imul(E,K)|0,i=i+Math.imul(w,G)|0,n=(n=n+Math.imul(w,Y)|0)+Math.imul(M,G)|0,o=o+Math.imul(M,Y)|0,i=i+Math.imul(v,$)|0,n=(n=n+Math.imul(v,X)|0)+Math.imul(b,$)|0,o=o+Math.imul(b,X)|0,i=i+Math.imul(p,Q)|0,n=(n=n+Math.imul(p,tt)|0)+Math.imul(m,Q)|0,o=o+Math.imul(m,tt)|0;var Mt=(u+(i=i+Math.imul(l,rt)|0)|0)+((8191&(n=(n=n+Math.imul(l,it)|0)+Math.imul(d,rt)|0))<<13)|0;u=((o=o+Math.imul(d,it)|0)+(n>>>13)|0)+(Mt>>>26)|0,Mt&=67108863,i=Math.imul(T,q),n=(n=Math.imul(T,F))+Math.imul(x,q)|0,o=Math.imul(x,F),i=i+Math.imul(k,H)|0,n=(n=n+Math.imul(k,K)|0)+Math.imul(B,H)|0,o=o+Math.imul(B,K)|0,i=i+Math.imul(A,G)|0,n=(n=n+Math.imul(A,Y)|0)+Math.imul(E,G)|0,o=o+Math.imul(E,Y)|0,i=i+Math.imul(w,$)|0,n=(n=n+Math.imul(w,X)|0)+Math.imul(M,$)|0,o=o+Math.imul(M,X)|0,i=i+Math.imul(v,Q)|0,n=(n=n+Math.imul(v,tt)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,tt)|0,i=i+Math.imul(p,rt)|0,n=(n=n+Math.imul(p,it)|0)+Math.imul(m,rt)|0,o=o+Math.imul(m,it)|0;var _t=(u+(i=i+Math.imul(l,ot)|0)|0)+((8191&(n=(n=n+Math.imul(l,st)|0)+Math.imul(d,ot)|0))<<13)|0;u=((o=o+Math.imul(d,st)|0)+(n>>>13)|0)+(_t>>>26)|0,_t&=67108863,i=Math.imul(C,q),n=(n=Math.imul(C,F))+Math.imul(P,q)|0,o=Math.imul(P,F),i=i+Math.imul(T,H)|0,n=(n=n+Math.imul(T,K)|0)+Math.imul(x,H)|0,o=o+Math.imul(x,K)|0,i=i+Math.imul(k,G)|0,n=(n=n+Math.imul(k,Y)|0)+Math.imul(B,G)|0,o=o+Math.imul(B,Y)|0,i=i+Math.imul(A,$)|0,n=(n=n+Math.imul(A,X)|0)+Math.imul(E,$)|0,o=o+Math.imul(E,X)|0,i=i+Math.imul(w,Q)|0,n=(n=n+Math.imul(w,tt)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,tt)|0,i=i+Math.imul(v,rt)|0,n=(n=n+Math.imul(v,it)|0)+Math.imul(b,rt)|0,o=o+Math.imul(b,it)|0,i=i+Math.imul(p,ot)|0,n=(n=n+Math.imul(p,st)|0)+Math.imul(m,ot)|0,o=o+Math.imul(m,st)|0;var At=(u+(i=i+Math.imul(l,ht)|0)|0)+((8191&(n=(n=n+Math.imul(l,ut)|0)+Math.imul(d,ht)|0))<<13)|0;u=((o=o+Math.imul(d,ut)|0)+(n>>>13)|0)+(At>>>26)|0,At&=67108863,i=Math.imul(L,q),n=(n=Math.imul(L,F))+Math.imul(O,q)|0,o=Math.imul(O,F),i=i+Math.imul(C,H)|0,n=(n=n+Math.imul(C,K)|0)+Math.imul(P,H)|0,o=o+Math.imul(P,K)|0,i=i+Math.imul(T,G)|0,n=(n=n+Math.imul(T,Y)|0)+Math.imul(x,G)|0,o=o+Math.imul(x,Y)|0,i=i+Math.imul(k,$)|0,n=(n=n+Math.imul(k,X)|0)+Math.imul(B,$)|0,o=o+Math.imul(B,X)|0,i=i+Math.imul(A,Q)|0,n=(n=n+Math.imul(A,tt)|0)+Math.imul(E,Q)|0,o=o+Math.imul(E,tt)|0,i=i+Math.imul(w,rt)|0,n=(n=n+Math.imul(w,it)|0)+Math.imul(M,rt)|0,o=o+Math.imul(M,it)|0,i=i+Math.imul(v,ot)|0,n=(n=n+Math.imul(v,st)|0)+Math.imul(b,ot)|0,o=o+Math.imul(b,st)|0,i=i+Math.imul(p,ht)|0,n=(n=n+Math.imul(p,ut)|0)+Math.imul(m,ht)|0,o=o+Math.imul(m,ut)|0;var Et=(u+(i=i+Math.imul(l,lt)|0)|0)+((8191&(n=(n=n+Math.imul(l,dt)|0)+Math.imul(d,lt)|0))<<13)|0;u=((o=o+Math.imul(d,dt)|0)+(n>>>13)|0)+(Et>>>26)|0,Et&=67108863,i=Math.imul(U,q),n=(n=Math.imul(U,F))+Math.imul(N,q)|0,o=Math.imul(N,F),i=i+Math.imul(L,H)|0,n=(n=n+Math.imul(L,K)|0)+Math.imul(O,H)|0,o=o+Math.imul(O,K)|0,i=i+Math.imul(C,G)|0,n=(n=n+Math.imul(C,Y)|0)+Math.imul(P,G)|0,o=o+Math.imul(P,Y)|0,i=i+Math.imul(T,$)|0,n=(n=n+Math.imul(T,X)|0)+Math.imul(x,$)|0,o=o+Math.imul(x,X)|0,i=i+Math.imul(k,Q)|0,n=(n=n+Math.imul(k,tt)|0)+Math.imul(B,Q)|0,o=o+Math.imul(B,tt)|0,i=i+Math.imul(A,rt)|0,n=(n=n+Math.imul(A,it)|0)+Math.imul(E,rt)|0,o=o+Math.imul(E,it)|0,i=i+Math.imul(w,ot)|0,n=(n=n+Math.imul(w,st)|0)+Math.imul(M,ot)|0,o=o+Math.imul(M,st)|0,i=i+Math.imul(v,ht)|0,n=(n=n+Math.imul(v,ut)|0)+Math.imul(b,ht)|0,o=o+Math.imul(b,ut)|0,i=i+Math.imul(p,lt)|0,n=(n=n+Math.imul(p,dt)|0)+Math.imul(m,lt)|0,o=o+Math.imul(m,dt)|0;var St=(u+(i=i+Math.imul(l,pt)|0)|0)+((8191&(n=(n=n+Math.imul(l,mt)|0)+Math.imul(d,pt)|0))<<13)|0;u=((o=o+Math.imul(d,mt)|0)+(n>>>13)|0)+(St>>>26)|0,St&=67108863,i=Math.imul(U,H),n=(n=Math.imul(U,K))+Math.imul(N,H)|0,o=Math.imul(N,K),i=i+Math.imul(L,G)|0,n=(n=n+Math.imul(L,Y)|0)+Math.imul(O,G)|0,o=o+Math.imul(O,Y)|0,i=i+Math.imul(C,$)|0,n=(n=n+Math.imul(C,X)|0)+Math.imul(P,$)|0,o=o+Math.imul(P,X)|0,i=i+Math.imul(T,Q)|0,n=(n=n+Math.imul(T,tt)|0)+Math.imul(x,Q)|0,o=o+Math.imul(x,tt)|0,i=i+Math.imul(k,rt)|0,n=(n=n+Math.imul(k,it)|0)+Math.imul(B,rt)|0,o=o+Math.imul(B,it)|0,i=i+Math.imul(A,ot)|0,n=(n=n+Math.imul(A,st)|0)+Math.imul(E,ot)|0,o=o+Math.imul(E,st)|0,i=i+Math.imul(w,ht)|0,n=(n=n+Math.imul(w,ut)|0)+Math.imul(M,ht)|0,o=o+Math.imul(M,ut)|0,i=i+Math.imul(v,lt)|0,n=(n=n+Math.imul(v,dt)|0)+Math.imul(b,lt)|0,o=o+Math.imul(b,dt)|0;var kt=(u+(i=i+Math.imul(p,pt)|0)|0)+((8191&(n=(n=n+Math.imul(p,mt)|0)+Math.imul(m,pt)|0))<<13)|0;u=((o=o+Math.imul(m,mt)|0)+(n>>>13)|0)+(kt>>>26)|0,kt&=67108863,i=Math.imul(U,G),n=(n=Math.imul(U,Y))+Math.imul(N,G)|0,o=Math.imul(N,Y),i=i+Math.imul(L,$)|0,n=(n=n+Math.imul(L,X)|0)+Math.imul(O,$)|0,o=o+Math.imul(O,X)|0,i=i+Math.imul(C,Q)|0,n=(n=n+Math.imul(C,tt)|0)+Math.imul(P,Q)|0,o=o+Math.imul(P,tt)|0,i=i+Math.imul(T,rt)|0,n=(n=n+Math.imul(T,it)|0)+Math.imul(x,rt)|0,o=o+Math.imul(x,it)|0,i=i+Math.imul(k,ot)|0,n=(n=n+Math.imul(k,st)|0)+Math.imul(B,ot)|0,o=o+Math.imul(B,st)|0,i=i+Math.imul(A,ht)|0,n=(n=n+Math.imul(A,ut)|0)+Math.imul(E,ht)|0,o=o+Math.imul(E,ut)|0,i=i+Math.imul(w,lt)|0,n=(n=n+Math.imul(w,dt)|0)+Math.imul(M,lt)|0,o=o+Math.imul(M,dt)|0;var Bt=(u+(i=i+Math.imul(v,pt)|0)|0)+((8191&(n=(n=n+Math.imul(v,mt)|0)+Math.imul(b,pt)|0))<<13)|0;u=((o=o+Math.imul(b,mt)|0)+(n>>>13)|0)+(Bt>>>26)|0,Bt&=67108863,i=Math.imul(U,$),n=(n=Math.imul(U,X))+Math.imul(N,$)|0,o=Math.imul(N,X),i=i+Math.imul(L,Q)|0,n=(n=n+Math.imul(L,tt)|0)+Math.imul(O,Q)|0,o=o+Math.imul(O,tt)|0,i=i+Math.imul(C,rt)|0,n=(n=n+Math.imul(C,it)|0)+Math.imul(P,rt)|0,o=o+Math.imul(P,it)|0,i=i+Math.imul(T,ot)|0,n=(n=n+Math.imul(T,st)|0)+Math.imul(x,ot)|0,o=o+Math.imul(x,st)|0,i=i+Math.imul(k,ht)|0,n=(n=n+Math.imul(k,ut)|0)+Math.imul(B,ht)|0,o=o+Math.imul(B,ut)|0,i=i+Math.imul(A,lt)|0,n=(n=n+Math.imul(A,dt)|0)+Math.imul(E,lt)|0,o=o+Math.imul(E,dt)|0;var Rt=(u+(i=i+Math.imul(w,pt)|0)|0)+((8191&(n=(n=n+Math.imul(w,mt)|0)+Math.imul(M,pt)|0))<<13)|0;u=((o=o+Math.imul(M,mt)|0)+(n>>>13)|0)+(Rt>>>26)|0,Rt&=67108863,i=Math.imul(U,Q),n=(n=Math.imul(U,tt))+Math.imul(N,Q)|0,o=Math.imul(N,tt),i=i+Math.imul(L,rt)|0,n=(n=n+Math.imul(L,it)|0)+Math.imul(O,rt)|0,o=o+Math.imul(O,it)|0,i=i+Math.imul(C,ot)|0,n=(n=n+Math.imul(C,st)|0)+Math.imul(P,ot)|0,o=o+Math.imul(P,st)|0,i=i+Math.imul(T,ht)|0,n=(n=n+Math.imul(T,ut)|0)+Math.imul(x,ht)|0,o=o+Math.imul(x,ut)|0,i=i+Math.imul(k,lt)|0,n=(n=n+Math.imul(k,dt)|0)+Math.imul(B,lt)|0,o=o+Math.imul(B,dt)|0;var Tt=(u+(i=i+Math.imul(A,pt)|0)|0)+((8191&(n=(n=n+Math.imul(A,mt)|0)+Math.imul(E,pt)|0))<<13)|0;u=((o=o+Math.imul(E,mt)|0)+(n>>>13)|0)+(Tt>>>26)|0,Tt&=67108863,i=Math.imul(U,rt),n=(n=Math.imul(U,it))+Math.imul(N,rt)|0,o=Math.imul(N,it),i=i+Math.imul(L,ot)|0,n=(n=n+Math.imul(L,st)|0)+Math.imul(O,ot)|0,o=o+Math.imul(O,st)|0,i=i+Math.imul(C,ht)|0,n=(n=n+Math.imul(C,ut)|0)+Math.imul(P,ht)|0,o=o+Math.imul(P,ut)|0,i=i+Math.imul(T,lt)|0,n=(n=n+Math.imul(T,dt)|0)+Math.imul(x,lt)|0,o=o+Math.imul(x,dt)|0;var xt=(u+(i=i+Math.imul(k,pt)|0)|0)+((8191&(n=(n=n+Math.imul(k,mt)|0)+Math.imul(B,pt)|0))<<13)|0;u=((o=o+Math.imul(B,mt)|0)+(n>>>13)|0)+(xt>>>26)|0,xt&=67108863,i=Math.imul(U,ot),n=(n=Math.imul(U,st))+Math.imul(N,ot)|0,o=Math.imul(N,st),i=i+Math.imul(L,ht)|0,n=(n=n+Math.imul(L,ut)|0)+Math.imul(O,ht)|0,o=o+Math.imul(O,ut)|0,i=i+Math.imul(C,lt)|0,n=(n=n+Math.imul(C,dt)|0)+Math.imul(P,lt)|0,o=o+Math.imul(P,dt)|0;var It=(u+(i=i+Math.imul(T,pt)|0)|0)+((8191&(n=(n=n+Math.imul(T,mt)|0)+Math.imul(x,pt)|0))<<13)|0;u=((o=o+Math.imul(x,mt)|0)+(n>>>13)|0)+(It>>>26)|0,It&=67108863,i=Math.imul(U,ht),n=(n=Math.imul(U,ut))+Math.imul(N,ht)|0,o=Math.imul(N,ut),i=i+Math.imul(L,lt)|0,n=(n=n+Math.imul(L,dt)|0)+Math.imul(O,lt)|0,o=o+Math.imul(O,dt)|0;var Ct=(u+(i=i+Math.imul(C,pt)|0)|0)+((8191&(n=(n=n+Math.imul(C,mt)|0)+Math.imul(P,pt)|0))<<13)|0;u=((o=o+Math.imul(P,mt)|0)+(n>>>13)|0)+(Ct>>>26)|0,Ct&=67108863,i=Math.imul(U,lt),n=(n=Math.imul(U,dt))+Math.imul(N,lt)|0,o=Math.imul(N,dt);var Pt=(u+(i=i+Math.imul(L,pt)|0)|0)+((8191&(n=(n=n+Math.imul(L,mt)|0)+Math.imul(O,pt)|0))<<13)|0;u=((o=o+Math.imul(O,mt)|0)+(n>>>13)|0)+(Pt>>>26)|0,Pt&=67108863;var Dt=(u+(i=Math.imul(U,pt))|0)+((8191&(n=(n=Math.imul(U,mt))+Math.imul(N,pt)|0))<<13)|0;return u=((o=Math.imul(N,mt))+(n>>>13)|0)+(Dt>>>26)|0,Dt&=67108863,h[0]=gt,h[1]=vt,h[2]=bt,h[3]=yt,h[4]=wt,h[5]=Mt,h[6]=_t,h[7]=At,h[8]=Et,h[9]=St,h[10]=kt,h[11]=Bt,h[12]=Rt,h[13]=Tt,h[14]=xt,h[15]=It,h[16]=Ct,h[17]=Pt,h[18]=Dt,0!==u&&(h[19]=u,r.length++),r};function p(t,e,r){return(new m).mulp(t,e,r)}function m(t,e){this.x=t,this.y=e}Math.imul||(c=d),n.prototype.mulTo=function(t,e){var r,i=this.length+t.length;return r=10===this.length&&10===t.length?c(this,t,e):i<63?d(this,t,e):i<1024?function(t,e,r){r.negative=e.negative^t.negative,r.length=t.length+e.length;for(var i=0,n=0,o=0;o>>26)|0)>>>26,s&=67108863}r.words[o]=a,i=s,s=n}return 0!==i?r.words[o]=i:r.length--,r.strip()}(this,t,e):p(this,t,e),r},m.prototype.makeRBT=function(t){for(var e=new Array(t),r=n.prototype._countBits(t)-1,i=0;i>=1;return i},m.prototype.permute=function(t,e,r,i,n,o){for(var s=0;s>>=1)n++;return 1<>>=13,i[2*s+1]=8191&o,o>>>=13;for(s=2*e;s>=26,e+=n/67108864|0,e+=o>>>26,this.words[i]=67108863&o}return 0!==e&&(this.words[i]=e,this.length++),this},n.prototype.muln=function(t){return this.clone().imuln(t)},n.prototype.sqr=function(){return this.mul(this)},n.prototype.isqr=function(){return this.imul(this.clone())},n.prototype.pow=function(t){var e=function(t){for(var e=new Array(t.bitLength()),r=0;r>>n}return e}(t);if(0===e.length)return new n(1);for(var r=this,i=0;i=0);var e,i=t%26,n=(t-i)/26,o=67108863>>>26-i<<26-i;if(0!==i){var s=0;for(e=0;e>>26-i}s&&(this.words[e]=s,this.length++)}if(0!==n){for(e=this.length-1;e>=0;e--)this.words[e+n]=this.words[e];for(e=0;e=0),n=e?(e-e%26)/26:0;var o=t%26,s=Math.min((t-o)/26,this.length),a=67108863^67108863>>>o<s)for(this.length-=s,u=0;u=0&&(0!==f||u>=n);u--){var l=0|this.words[u];this.words[u]=f<<26-o|l>>>o,f=l&a}return h&&0!==f&&(h.words[h.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},n.prototype.ishrn=function(t,e,i){return r(0===this.negative),this.iushrn(t,e,i)},n.prototype.shln=function(t){return this.clone().ishln(t)},n.prototype.ushln=function(t){return this.clone().iushln(t)},n.prototype.shrn=function(t){return this.clone().ishrn(t)},n.prototype.ushrn=function(t){return this.clone().iushrn(t)},n.prototype.testn=function(t){r("number"==typeof t&&t>=0);var e=t%26,i=(t-e)/26,n=1<=0);var e=t%26,i=(t-e)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=i)return this;if(0!==e&&i++,this.length=Math.min(i,this.length),0!==e){var n=67108863^67108863>>>e<=67108864;e++)this.words[e]-=67108864,e===this.length-1?this.words[e+1]=1:this.words[e+1]++;return this.length=Math.max(this.length,e+1),this},n.prototype.isubn=function(t){if(r("number"==typeof t),r(t<67108864),t<0)return this.iaddn(-t);if(0!==this.negative)return this.negative=0,this.iaddn(t),this.negative=1,this;if(this.words[0]-=t,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var e=0;e>26)-(h/67108864|0),this.words[n+i]=67108863&o}for(;n>26,this.words[n+i]=67108863&o;if(0===a)return this.strip();for(r(-1===a),a=0,n=0;n>26,this.words[n]=67108863&o;return this.negative=1,this.strip()},n.prototype._wordDiv=function(t,e){var r=(this.length,t.length),i=this.clone(),o=t,s=0|o.words[o.length-1];0!==(r=26-this._countBits(s))&&(o=o.ushln(r),i.iushln(r),s=0|o.words[o.length-1]);var a,h=i.length-o.length;if("mod"!==e){(a=new n(null)).length=h+1,a.words=new Array(a.length);for(var u=0;u=0;l--){var d=67108864*(0|i.words[o.length+l])+(0|i.words[o.length+l-1]);for(d=Math.min(d/s|0,67108863),i._ishlnsubmul(o,d,l);0!==i.negative;)d--,i.negative=0,i._ishlnsubmul(o,1,l),i.isZero()||(i.negative^=1);a&&(a.words[l]=d)}return a&&a.strip(),i.strip(),"div"!==e&&0!==r&&i.iushrn(r),{div:a||null,mod:i}},n.prototype.divmod=function(t,e,i){return r(!t.isZero()),this.isZero()?{div:new n(0),mod:new n(0)}:0!==this.negative&&0===t.negative?(a=this.neg().divmod(t,e),"mod"!==e&&(o=a.div.neg()),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.iadd(t)),{div:o,mod:s}):0===this.negative&&0!==t.negative?(a=this.divmod(t.neg(),e),"mod"!==e&&(o=a.div.neg()),{div:o,mod:a.mod}):0!=(this.negative&t.negative)?(a=this.neg().divmod(t.neg(),e),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.isub(t)),{div:a.div,mod:s}):t.length>this.length||this.cmp(t)<0?{div:new n(0),mod:this}:1===t.length?"div"===e?{div:this.divn(t.words[0]),mod:null}:"mod"===e?{div:null,mod:new n(this.modn(t.words[0]))}:{div:this.divn(t.words[0]),mod:new n(this.modn(t.words[0]))}:this._wordDiv(t,e);var o,s,a},n.prototype.div=function(t){return this.divmod(t,"div",!1).div},n.prototype.mod=function(t){return this.divmod(t,"mod",!1).mod},n.prototype.umod=function(t){return this.divmod(t,"mod",!0).mod},n.prototype.divRound=function(t){var e=this.divmod(t);if(e.mod.isZero())return e.div;var r=0!==e.div.negative?e.mod.isub(t):e.mod,i=t.ushrn(1),n=t.andln(1),o=r.cmp(i);return o<0||1===n&&0===o?e.div:0!==e.div.negative?e.div.isubn(1):e.div.iaddn(1)},n.prototype.modn=function(t){r(t<=67108863);for(var e=(1<<26)%t,i=0,n=this.length-1;n>=0;n--)i=(e*i+(0|this.words[n]))%t;return i},n.prototype.idivn=function(t){r(t<=67108863);for(var e=0,i=this.length-1;i>=0;i--){var n=(0|this.words[i])+67108864*e;this.words[i]=n/t|0,e=n%t}return this.strip()},n.prototype.divn=function(t){return this.clone().idivn(t)},n.prototype.egcd=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o=new n(1),s=new n(0),a=new n(0),h=new n(1),u=0;e.isEven()&&i.isEven();)e.iushrn(1),i.iushrn(1),++u;for(var f=i.clone(),l=e.clone();!e.isZero();){for(var d=0,c=1;0==(e.words[0]&c)&&d<26;++d,c<<=1);if(d>0)for(e.iushrn(d);d-- >0;)(o.isOdd()||s.isOdd())&&(o.iadd(f),s.isub(l)),o.iushrn(1),s.iushrn(1);for(var p=0,m=1;0==(i.words[0]&m)&&p<26;++p,m<<=1);if(p>0)for(i.iushrn(p);p-- >0;)(a.isOdd()||h.isOdd())&&(a.iadd(f),h.isub(l)),a.iushrn(1),h.iushrn(1);e.cmp(i)>=0?(e.isub(i),o.isub(a),s.isub(h)):(i.isub(e),a.isub(o),h.isub(s))}return{a:a,b:h,gcd:i.iushln(u)}},n.prototype._invmp=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o,s=new n(1),a=new n(0),h=i.clone();e.cmpn(1)>0&&i.cmpn(1)>0;){for(var u=0,f=1;0==(e.words[0]&f)&&u<26;++u,f<<=1);if(u>0)for(e.iushrn(u);u-- >0;)s.isOdd()&&s.iadd(h),s.iushrn(1);for(var l=0,d=1;0==(i.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(i.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(h),a.iushrn(1);e.cmp(i)>=0?(e.isub(i),s.isub(a)):(i.isub(e),a.isub(s))}return(o=0===e.cmpn(1)?s:a).cmpn(0)<0&&o.iadd(t),o},n.prototype.gcd=function(t){if(this.isZero())return t.abs();if(t.isZero())return this.abs();var e=this.clone(),r=t.clone();e.negative=0,r.negative=0;for(var i=0;e.isEven()&&r.isEven();i++)e.iushrn(1),r.iushrn(1);for(;;){for(;e.isEven();)e.iushrn(1);for(;r.isEven();)r.iushrn(1);var n=e.cmp(r);if(n<0){var o=e;e=r,r=o}else if(0===n||0===r.cmpn(1))break;e.isub(r)}return r.iushln(i)},n.prototype.invm=function(t){return this.egcd(t).a.umod(t)},n.prototype.isEven=function(){return 0==(1&this.words[0])},n.prototype.isOdd=function(){return 1==(1&this.words[0])},n.prototype.andln=function(t){return this.words[0]&t},n.prototype.bincn=function(t){r("number"==typeof t);var e=t%26,i=(t-e)/26,n=1<>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},n.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},n.prototype.cmpn=function(t){var e,i=t<0;if(0!==this.negative&&!i)return-1;if(0===this.negative&&i)return 1;if(this.strip(),this.length>1)e=1;else{i&&(t=-t),r(t<=67108863,"Number is too big");var n=0|this.words[0];e=n===t?0:nt.length)return 1;if(this.length=0;r--){var i=0|this.words[r],n=0|t.words[r];if(i!==n){in&&(e=1);break}}return e},n.prototype.gtn=function(t){return 1===this.cmpn(t)},n.prototype.gt=function(t){return 1===this.cmp(t)},n.prototype.gten=function(t){return this.cmpn(t)>=0},n.prototype.gte=function(t){return this.cmp(t)>=0},n.prototype.ltn=function(t){return-1===this.cmpn(t)},n.prototype.lt=function(t){return-1===this.cmp(t)},n.prototype.lten=function(t){return this.cmpn(t)<=0},n.prototype.lte=function(t){return this.cmp(t)<=0},n.prototype.eqn=function(t){return 0===this.cmpn(t)},n.prototype.eq=function(t){return 0===this.cmp(t)},n.red=function(t){return new _(t)},n.prototype.toRed=function(t){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),t.convertTo(this)._forceRed(t)},n.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},n.prototype._forceRed=function(t){return this.red=t,this},n.prototype.forceRed=function(t){return r(!this.red,"Already a number in reduction context"),this._forceRed(t)},n.prototype.redAdd=function(t){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,t)},n.prototype.redIAdd=function(t){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,t)},n.prototype.redSub=function(t){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,t)},n.prototype.redISub=function(t){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,t)},n.prototype.redShl=function(t){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,t)},n.prototype.redMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.mul(this,t)},n.prototype.redIMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.imul(this,t)},n.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},n.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},n.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},n.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},n.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},n.prototype.redPow=function(t){return r(this.red&&!t.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,t)};var g={k256:null,p224:null,p192:null,p25519:null};function v(t,e){this.name=t,this.p=new n(e,16),this.n=this.p.bitLength(),this.k=new n(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function b(){v.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function y(){v.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function w(){v.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function M(){v.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function _(t){if("string"==typeof t){var e=n._prime(t);this.m=e.p,this.prime=e}else r(t.gtn(1),"modulus must be greater than 1"),this.m=t,this.prime=null}function A(t){_.call(this,t),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new n(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}v.prototype._tmp=function(){var t=new n(null);return t.words=new Array(Math.ceil(this.n/13)),t},v.prototype.ireduce=function(t){var e,r=t;do{this.split(r,this.tmp),e=(r=(r=this.imulK(r)).iadd(this.tmp)).bitLength()}while(e>this.n);var i=e0?r.isub(this.p):void 0!==r.strip?r.strip():r._strip(),r},v.prototype.split=function(t,e){t.iushrn(this.n,0,e)},v.prototype.imulK=function(t){return t.imul(this.k)},i(b,v),b.prototype.split=function(t,e){for(var r=4194303,i=Math.min(t.length,9),n=0;n>>22,o=s}o>>>=22,t.words[n-10]=o,0===o&&t.length>10?t.length-=10:t.length-=9},b.prototype.imulK=function(t){t.words[t.length]=0,t.words[t.length+1]=0,t.length+=2;for(var e=0,r=0;r>>=26,t.words[r]=n,e=i}return 0!==e&&(t.words[t.length++]=e),t},n._prime=function(t){if(g[t])return g[t];var e;if("k256"===t)e=new b;else if("p224"===t)e=new y;else if("p192"===t)e=new w;else{if("p25519"!==t)throw new Error("Unknown prime "+t);e=new M}return g[t]=e,e},_.prototype._verify1=function(t){r(0===t.negative,"red works only with positives"),r(t.red,"red works only with red numbers")},_.prototype._verify2=function(t,e){r(0==(t.negative|e.negative),"red works only with positives"),r(t.red&&t.red===e.red,"red works only with red numbers")},_.prototype.imod=function(t){return this.prime?this.prime.ireduce(t)._forceRed(this):t.umod(this.m)._forceRed(this)},_.prototype.neg=function(t){return t.isZero()?t.clone():this.m.sub(t)._forceRed(this)},_.prototype.add=function(t,e){this._verify2(t,e);var r=t.add(e);return r.cmp(this.m)>=0&&r.isub(this.m),r._forceRed(this)},_.prototype.iadd=function(t,e){this._verify2(t,e);var r=t.iadd(e);return r.cmp(this.m)>=0&&r.isub(this.m),r},_.prototype.sub=function(t,e){this._verify2(t,e);var r=t.sub(e);return r.cmpn(0)<0&&r.iadd(this.m),r._forceRed(this)},_.prototype.isub=function(t,e){this._verify2(t,e);var r=t.isub(e);return r.cmpn(0)<0&&r.iadd(this.m),r},_.prototype.shl=function(t,e){return this._verify1(t),this.imod(t.ushln(e))},_.prototype.imul=function(t,e){return this._verify2(t,e),this.imod(t.imul(e))},_.prototype.mul=function(t,e){return this._verify2(t,e),this.imod(t.mul(e))},_.prototype.isqr=function(t){return this.imul(t,t.clone())},_.prototype.sqr=function(t){return this.mul(t,t)},_.prototype.sqrt=function(t){if(t.isZero())return t.clone();var e=this.m.andln(3);if(r(e%2==1),3===e){var i=this.m.add(new n(1)).iushrn(2);return this.pow(t,i)}for(var o=this.m.subn(1),s=0;!o.isZero()&&0===o.andln(1);)s++,o.iushrn(1);r(!o.isZero());var a=new n(1).toRed(this),h=a.redNeg(),u=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new n(2*f*f).toRed(this);0!==this.pow(f,u).cmp(h);)f.redIAdd(h);for(var l=this.pow(f,o),d=this.pow(t,o.addn(1).iushrn(1)),c=this.pow(t,o),p=s;0!==c.cmp(a);){for(var m=c,g=0;0!==m.cmp(a);g++)m=m.redSqr();r(g=0;i--){for(var u=e.words[i],f=h-1;f>=0;f--){var l=u>>f&1;o!==r[0]&&(o=this.sqr(o)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===i&&0===f)&&(o=this.mul(o,r[s]),a=0,s=0)):a=0}h=26}return o},_.prototype.convertTo=function(t){var e=t.umod(this.m);return e===t?e.clone():e},_.prototype.convertFrom=function(t){var e=t.clone();return e.red=null,e},n.mont=function(t){return new A(t)},i(A,_),A.prototype.convertTo=function(t){return this.imod(t.ushln(this.shift))},A.prototype.convertFrom=function(t){var e=this.imod(t.mul(this.rinv));return e.red=null,e},A.prototype.imul=function(t,e){if(t.isZero()||e.isZero())return t.words[0]=0,t.length=1,t;var r=t.imul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),n=r.isub(i).iushrn(this.shift),o=n;return n.cmp(this.m)>=0?o=n.isub(this.m):n.cmpn(0)<0&&(o=n.iadd(this.m)),o._forceRed(this)},A.prototype.mul=function(t,e){if(t.isZero()||e.isZero())return new n(0)._forceRed(this);var r=t.mul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),o=r.isub(i).iushrn(this.shift),s=o;return o.cmp(this.m)>=0?s=o.isub(this.m):o.cmpn(0)<0&&(s=o.iadd(this.m)),s._forceRed(this)},A.prototype.invm=function(t){return this.imod(t._invmp(this.m).mul(this.r2))._forceRed(this)}}(void 0===t||t,this),t.exports}.call({})}var bm,ym,wm,Mm=!1;function _m(t){this.rand=t}function Am(){return Mm||(Mm=!0,function(){if(bm={},bm=function(t){return ym||(ym=new _m(null)),ym.generate(t)},wm=_m,bm.Rand=wm,_m.prototype.generate=function(t){return this._rand(t)},_m.prototype._rand=function(t){if(this.rand.getBytes)return this.rand.getBytes(t);for(var e=new Uint8Array(t),r=0;r=0);return i},Bm.prototype._randrange=function(t,e){var r=e.sub(t);return t.add(this._randbelow(r))},Bm.prototype.test=function(t,e,r){var i=t.bitLength(),n=Sm.mont(t),o=new Sm(1).toRed(n);e||(e=Math.max(1,i/48|0));for(var s=t.subn(1),a=0;!s.testn(a);a++);for(var h=t.shrn(a),u=s.toRed(n);e>0;e--){var f=this._randrange(new Sm(2),s);r&&r(f);var l=f.toRed(n).redPow(h);if(0!==l.cmp(o)&&0!==l.cmp(u)){for(var d=1;d0;e--){var u=this._randrange(new Sm(2),o),f=t.gcd(u);if(0!==f.cmpn(1))return f;var l=u.toRed(i).redPow(a);if(0!==l.cmp(n)&&0!==l.cmp(h)){for(var d=1;dt;)r.ishrn(1);if(r.isEven()&&r.iadd(Om),r.testn(1)||r.iadd(jm),e.cmp(jm)){if(!e.cmp(Um))for(;r.mod(Nm).cmp(zm);)r.iadd(Fm)}else for(;r.mod(Pm).cmp(qm);)r.iadd(Fm);if(Km(i=r.shrn(1))&&Km(r)&&Wm(i)&&Wm(r)&&Lm.test(i)&&Lm.test(r))return r}}function Ym(){return Hm||(Hm=!0,xm={},Im=wr(),xm=Gm,Gm.simpleSieve=Km,Gm.fermatTest=Wm,Cm=pm(),Pm=new Cm(24),Dm=Tm(),Lm=new Dm,Om=new Cm(1),jm=new Cm(2),Um=new Cm(5),new Cm(16),new Cm(8),Nm=new Cm(10),zm=new Cm(3),new Cm(7),qm=new Cm(11),Fm=new Cm(4),new Cm(12),Zm=null),xm}var Vm,$m=!1;function Xm(){return $m||($m=!0,Vm={},Vm=JSON.parse('{"modp1":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a63a3620ffffffffffffffff"},"modp2":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff"},"modp5":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff"},"modp14":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff"},"modp15":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a93ad2caffffffffffffffff"},"modp16":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c934063199ffffffffffffffff"},"modp17":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dcc4024ffffffffffffffff"},"modp18":{"gen":"02","prime":"ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aaac42dad33170d04507a33a85521abdf1cba64ecfb850458dbef0a8aea71575d060c7db3970f85a6e1e4c7abf5ae8cdb0933d71e8c94e04a25619dcee3d2261ad2ee6bf12ffa06d98a0864d87602733ec86a64521f2b18177b200cbbe117577a615d6c770988c0bad946e208e24fa074e5ab3143db5bfce0fd108e4b82d120a92108011a723c12a787e6d788719a10bdba5b2699c327186af4e23c1a946834b6150bda2583e9ca2ad44ce8dbbbc2db04de8ef92e8efc141fbecaa6287c59474e6bc05d99b2964fa090c3a2233ba186515be7ed1f612970cee2d7afb81bdd762170481cd0069127d5b05aa993b4ea988d8fddc186ffb7dc90a6c08f4df435c93402849236c3fab4d27c7026c1d4dcb2602646dec9751e763dba37bdf8ff9406ad9e530ee5db382f413001aeb06a53ed9027d831179727b0865a8918da3edbebcf9b14ed44ce6cbaced4bb1bdb7f1447e6cc254b332051512bd7af426fb8f401378cd2bf5983ca01c64b92ecf032ea15d1721d03f482d7ce6e74fef6d55e702f46980c82b5a84031900b1c9e59e7c97fbec7e8f323a97a7e36cc88be0f1d45b7ff585ac54bd407b22b4154aacc8f6d7ebf48e1d814cc5ed20f8037e0a79715eef29be32806a1d58bb7c5da76f550aa3d8a1fbff0eb19ccb1a313d55cda56c9ec2ef29632387fe8d76e3c0468043e8f663f4860ee12bf2d5b0b7474d6e694f91e6dbe115974a3926f12fee5e438777cb6a932df8cd8bec4d073b931ba3bc832b68d9dd300741fa7bf8afc47ed2576f6936ba424663aab639c5ae4f5683423b4742bf1c978238f16cbe39d652de3fdb8befc848ad922222e04a4037c0713eb57a81a23f0c73473fc646cea306b4bcbc8862f8385ddfa9d4b7fa2c087e879683303ed5bdd3a062b3cf5b3a278a66d2a13f83f44f82ddf310ee074ab6a364597e899a0255dc164f31cc50846851df9ab48195ded7ea1b1d510bd7ee74d73faf36bc31ecfa268359046f4eb879f924009438b481c6cd7889a002ed5ee382bc9190da6fc026e479558e4475677e9aa9e3050e2765694dfc81f56e880b96e7160c980dd98edd3dfffffffffffffffff"}}')),Vm}var Jm,Qm,tg,eg,rg,ig,ng,og,sg,ag,hg,ug,fg=!1;function lg(t,e){return e=e||"utf8",Qm.isBuffer(t)||(t=new Qm(t,e)),this._pub=new tg(t),this}function dg(t,e){return e=e||"utf8",Qm.isBuffer(t)||(t=new Qm(t,e)),this._priv=new tg(t),this}function cg(t,e,r){this.setGenerator(e),this.__prime=new tg(t),this._prime=tg.mont(this.__prime),this._primeLen=t.length,this._pub=void 0,this._priv=void 0,this._primeCode=void 0,r?(this.setPublicKey=lg,this.setPrivateKey=dg):this._primeCode=8}function pg(t,e){var r=new Qm(t.toArray());return e?r.toString(e):r}function mg(){Jm={},Qm=Ye().Buffer,tg=pm(),eg=Tm(),rg=new eg,ig=new tg(24),ng=new tg(11),og=new tg(10),sg=new tg(3),ag=new tg(7),Ym(),hg=wr(),Jm=cg,ug={},Object.defineProperty(cg.prototype,"verifyError",{enumerable:!0,get:function(){return"number"!=typeof this._primeCode&&(this._primeCode=function(t,e){var r=e.toString("hex"),i=[r,t.toString(16)].join("_");if(i in ug)return ug[i];var n,o=0;if(t.isEven()||!Ym().simpleSieve||!Ym().fermatTest(t)||!rg.test(t))return o+=1,o+="02"===r||"05"===r?8:4,ug[i]=o,o;switch(rg.test(t.shrn(1))||(o+=2),r){case"02":t.mod(ig).cmp(ng)&&(o+=8);break;case"05":(n=t.mod(og)).cmp(sg)&&n.cmp(ag)&&(o+=8);break;default:o+=4}return ug[i]=o,o}(this.__prime,this.__gen)),this._primeCode}}),cg.prototype.generateKeys=function(){return this._priv||(this._priv=new tg(hg(this._primeLen))),this._pub=this._gen.toRed(this._prime).redPow(this._priv).fromRed(),this.getPublicKey()},cg.prototype.computeSecret=function(t){var e=(t=(t=new tg(t)).toRed(this._prime)).redPow(this._priv).fromRed(),r=new Qm(e.toArray()),i=this.getPrime();if(r.length=48&&i<=57?i-48:i>=65&&i<=70?i-55:i>=97&&i<=102?i-87:void r(!1,"Invalid character in "+t)}function a(t,e,r){var i=s(t,r);return r-1>=e&&(i|=s(t,r-1)<<4),i}function h(t,e,i,n){for(var o=0,s=0,a=Math.min(t.length,i),h=e;h=49?u-49+10:u>=17?u-17+10:u,r(u>=0&&s0?t:e},n.min=function(t,e){return t.cmp(e)<0?t:e},n.prototype._init=function(t,e,i){if("number"==typeof t)return this._initNumber(t,e,i);if("object"==typeof t)return this._initArray(t,e,i);"hex"===e&&(e=16),r(e===(0|e)&&e>=2&&e<=36);var n=0;"-"===(t=t.toString().replace(/\s+/g,""))[0]&&(n++,this.negative=1),n=0;n-=3)s=t[n]|t[n-1]<<8|t[n-2]<<16,this.words[o]|=s<>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===i)for(n=0,o=0;n>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this._strip()},n.prototype._parseHex=function(t,e,r){this.length=Math.ceil((t.length-e)/6),this.words=new Array(this.length);for(var i=0;i=e;i-=2)n=a(t,e,i)<=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;else for(i=(t.length-e)%2==0?e+1:e;i=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;this._strip()},n.prototype._parseBase=function(t,e,r){this.words=[0],this.length=1;for(var i=0,n=1;n<=67108863;n*=e)i++;i--,n=n/e|0;for(var o=t.length-r,s=o%i,a=Math.min(o,o-s)+r,u=0,f=r;f1&&0===this.words[this.length-1];)this.length--;return this._normSign()},n.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},"undefined"!=typeof Symbol&&"function"==typeof Symbol.for)try{n.prototype[Symbol.for("nodejs.util.inspect.custom")]=f}catch(t){n.prototype.inspect=f}else n.prototype.inspect=f;function f(){return(this.red?""}var l=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],d=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],c=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];n.prototype.toString=function(t,e){var i;if(e=0|e||1,16===(t=t||10)||"hex"===t){i="";for(var n=0,o=0,s=0;s>>24-n&16777215)||s!==this.length-1?l[6-h.length]+h+i:h+i,(n+=2)>=26&&(n-=26,s--)}for(0!==o&&(i=o.toString(16)+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}if(t===(0|t)&&t>=2&&t<=36){var u=d[t],f=c[t];i="";var p=this.clone();for(p.negative=0;!p.isZero();){var m=p.modrn(f).toString(t);i=(p=p.idivn(f)).isZero()?m+i:l[u-m.length]+m+i}for(this.isZero()&&(i="0"+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}r(!1,"Base should be between 2 and 36")},n.prototype.toNumber=function(){var t=this.words[0];return 2===this.length?t+=67108864*this.words[1]:3===this.length&&1===this.words[2]?t+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-t:t},n.prototype.toJSON=function(){return this.toString(16,2)},o&&(n.prototype.toBuffer=function(t,e){return this.toArrayLike(o,t,e)}),n.prototype.toArray=function(t,e){return this.toArrayLike(Array,t,e)};function p(t,e,r){r.negative=e.negative^t.negative;var i=t.length+e.length|0;r.length=i,i=i-1|0;var n=0|t.words[0],o=0|e.words[0],s=n*o,a=67108863&s,h=s/67108864|0;r.words[0]=a;for(var u=1;u>>26,l=67108863&h,d=Math.min(u,e.length-1),c=Math.max(0,u-t.length+1);c<=d;c++){var p=u-c|0;f+=(s=(n=0|t.words[p])*(o=0|e.words[c])+l)/67108864|0,l=67108863&s}r.words[u]=0|l,h=0|f}return 0!==h?r.words[u]=0|h:r.length--,r._strip()}n.prototype.toArrayLike=function(t,e,i){this._strip();var n=this.byteLength(),o=i||Math.max(1,n);r(n<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0");var s=function(t,e){return t.allocUnsafe?t.allocUnsafe(e):new t(e)}(t,o);return this["_toArrayLike"+("le"===e?"LE":"BE")](s,n),s},n.prototype._toArrayLikeLE=function(t,e){for(var r=0,i=0,n=0,o=0;n>8&255),r>16&255),6===o?(r>24&255),i=0,o=0):(i=s>>>24,o+=2)}if(r=0&&(t[r--]=s>>8&255),r>=0&&(t[r--]=s>>16&255),6===o?(r>=0&&(t[r--]=s>>24&255),i=0,o=0):(i=s>>>24,o+=2)}if(r>=0)for(t[r--]=i;r>=0;)t[r--]=0},Math.clz32?n.prototype._countBits=function(t){return 32-Math.clz32(t)}:n.prototype._countBits=function(t){var e=t,r=0;return e>=4096&&(r+=13,e>>>=13),e>=64&&(r+=7,e>>>=7),e>=8&&(r+=4,e>>>=4),e>=2&&(r+=2,e>>>=2),r+e},n.prototype._zeroBits=function(t){if(0===t)return 26;var e=t,r=0;return 0==(8191&e)&&(r+=13,e>>>=13),0==(127&e)&&(r+=7,e>>>=7),0==(15&e)&&(r+=4,e>>>=4),0==(3&e)&&(r+=2,e>>>=2),0==(1&e)&&r++,r},n.prototype.bitLength=function(){var t=this.words[this.length-1],e=this._countBits(t);return 26*(this.length-1)+e},n.prototype.zeroBits=function(){if(this.isZero())return 0;for(var t=0,e=0;et.length?this.clone().ior(t):t.clone().ior(this)},n.prototype.uor=function(t){return this.length>t.length?this.clone().iuor(t):t.clone().iuor(this)},n.prototype.iuand=function(t){var e;e=this.length>t.length?t:this;for(var r=0;rt.length?this.clone().iand(t):t.clone().iand(this)},n.prototype.uand=function(t){return this.length>t.length?this.clone().iuand(t):t.clone().iuand(this)},n.prototype.iuxor=function(t){var e,r;this.length>t.length?(e=this,r=t):(e=t,r=this);for(var i=0;it.length?this.clone().ixor(t):t.clone().ixor(this)},n.prototype.uxor=function(t){return this.length>t.length?this.clone().iuxor(t):t.clone().iuxor(this)},n.prototype.inotn=function(t){r("number"==typeof t&&t>=0);var e=0|Math.ceil(t/26),i=t%26;this._expand(e),i>0&&e--;for(var n=0;n0&&(this.words[n]=~this.words[n]&67108863>>26-i),this._strip()},n.prototype.notn=function(t){return this.clone().inotn(t)},n.prototype.setn=function(t,e){r("number"==typeof t&&t>=0);var i=t/26|0,n=t%26;return this._expand(i+1),this.words[i]=e?this.words[i]|1<t.length?(r=this,i=t):(r=t,i=this);for(var n=0,o=0;o>>26;for(;0!==n&&o>>26;if(this.length=r.length,0!==n)this.words[this.length]=n,this.length++;else if(r!==this)for(;ot.length?this.clone().iadd(t):t.clone().iadd(this)},n.prototype.isub=function(t){if(0!==t.negative){t.negative=0;var e=this.iadd(t);return t.negative=1,e._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(t),this.negative=1,this._normSign();var r,i,n=this.cmp(t);if(0===n)return this.negative=0,this.length=1,this.words[0]=0,this;n>0?(r=this,i=t):(r=t,i=this);for(var o=0,s=0;s>26,this.words[s]=67108863&e;for(;0!==o&&s>26,this.words[s]=67108863&e;if(0===o&&s>>13,c=0|s[1],p=8191&c,m=c>>>13,g=0|s[2],v=8191&g,b=g>>>13,y=0|s[3],w=8191&y,M=y>>>13,_=0|s[4],A=8191&_,E=_>>>13,S=0|s[5],k=8191&S,B=S>>>13,R=0|s[6],T=8191&R,x=R>>>13,I=0|s[7],C=8191&I,P=I>>>13,D=0|s[8],L=8191&D,O=D>>>13,j=0|s[9],U=8191&j,N=j>>>13,z=0|a[0],q=8191&z,F=z>>>13,Z=0|a[1],H=8191&Z,K=Z>>>13,W=0|a[2],G=8191&W,Y=W>>>13,V=0|a[3],$=8191&V,X=V>>>13,J=0|a[4],Q=8191&J,tt=J>>>13,et=0|a[5],rt=8191&et,it=et>>>13,nt=0|a[6],ot=8191&nt,st=nt>>>13,at=0|a[7],ht=8191&at,ut=at>>>13,ft=0|a[8],lt=8191&ft,dt=ft>>>13,ct=0|a[9],pt=8191&ct,mt=ct>>>13;r.negative=t.negative^e.negative,r.length=19;var gt=(u+(i=Math.imul(l,q))|0)+((8191&(n=(n=Math.imul(l,F))+Math.imul(d,q)|0))<<13)|0;u=((o=Math.imul(d,F))+(n>>>13)|0)+(gt>>>26)|0,gt&=67108863,i=Math.imul(p,q),n=(n=Math.imul(p,F))+Math.imul(m,q)|0,o=Math.imul(m,F);var vt=(u+(i=i+Math.imul(l,H)|0)|0)+((8191&(n=(n=n+Math.imul(l,K)|0)+Math.imul(d,H)|0))<<13)|0;u=((o=o+Math.imul(d,K)|0)+(n>>>13)|0)+(vt>>>26)|0,vt&=67108863,i=Math.imul(v,q),n=(n=Math.imul(v,F))+Math.imul(b,q)|0,o=Math.imul(b,F),i=i+Math.imul(p,H)|0,n=(n=n+Math.imul(p,K)|0)+Math.imul(m,H)|0,o=o+Math.imul(m,K)|0;var bt=(u+(i=i+Math.imul(l,G)|0)|0)+((8191&(n=(n=n+Math.imul(l,Y)|0)+Math.imul(d,G)|0))<<13)|0;u=((o=o+Math.imul(d,Y)|0)+(n>>>13)|0)+(bt>>>26)|0,bt&=67108863,i=Math.imul(w,q),n=(n=Math.imul(w,F))+Math.imul(M,q)|0,o=Math.imul(M,F),i=i+Math.imul(v,H)|0,n=(n=n+Math.imul(v,K)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,K)|0,i=i+Math.imul(p,G)|0,n=(n=n+Math.imul(p,Y)|0)+Math.imul(m,G)|0,o=o+Math.imul(m,Y)|0;var yt=(u+(i=i+Math.imul(l,$)|0)|0)+((8191&(n=(n=n+Math.imul(l,X)|0)+Math.imul(d,$)|0))<<13)|0;u=((o=o+Math.imul(d,X)|0)+(n>>>13)|0)+(yt>>>26)|0,yt&=67108863,i=Math.imul(A,q),n=(n=Math.imul(A,F))+Math.imul(E,q)|0,o=Math.imul(E,F),i=i+Math.imul(w,H)|0,n=(n=n+Math.imul(w,K)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,K)|0,i=i+Math.imul(v,G)|0,n=(n=n+Math.imul(v,Y)|0)+Math.imul(b,G)|0,o=o+Math.imul(b,Y)|0,i=i+Math.imul(p,$)|0,n=(n=n+Math.imul(p,X)|0)+Math.imul(m,$)|0,o=o+Math.imul(m,X)|0;var wt=(u+(i=i+Math.imul(l,Q)|0)|0)+((8191&(n=(n=n+Math.imul(l,tt)|0)+Math.imul(d,Q)|0))<<13)|0;u=((o=o+Math.imul(d,tt)|0)+(n>>>13)|0)+(wt>>>26)|0,wt&=67108863,i=Math.imul(k,q),n=(n=Math.imul(k,F))+Math.imul(B,q)|0,o=Math.imul(B,F),i=i+Math.imul(A,H)|0,n=(n=n+Math.imul(A,K)|0)+Math.imul(E,H)|0,o=o+Math.imul(E,K)|0,i=i+Math.imul(w,G)|0,n=(n=n+Math.imul(w,Y)|0)+Math.imul(M,G)|0,o=o+Math.imul(M,Y)|0,i=i+Math.imul(v,$)|0,n=(n=n+Math.imul(v,X)|0)+Math.imul(b,$)|0,o=o+Math.imul(b,X)|0,i=i+Math.imul(p,Q)|0,n=(n=n+Math.imul(p,tt)|0)+Math.imul(m,Q)|0,o=o+Math.imul(m,tt)|0;var Mt=(u+(i=i+Math.imul(l,rt)|0)|0)+((8191&(n=(n=n+Math.imul(l,it)|0)+Math.imul(d,rt)|0))<<13)|0;u=((o=o+Math.imul(d,it)|0)+(n>>>13)|0)+(Mt>>>26)|0,Mt&=67108863,i=Math.imul(T,q),n=(n=Math.imul(T,F))+Math.imul(x,q)|0,o=Math.imul(x,F),i=i+Math.imul(k,H)|0,n=(n=n+Math.imul(k,K)|0)+Math.imul(B,H)|0,o=o+Math.imul(B,K)|0,i=i+Math.imul(A,G)|0,n=(n=n+Math.imul(A,Y)|0)+Math.imul(E,G)|0,o=o+Math.imul(E,Y)|0,i=i+Math.imul(w,$)|0,n=(n=n+Math.imul(w,X)|0)+Math.imul(M,$)|0,o=o+Math.imul(M,X)|0,i=i+Math.imul(v,Q)|0,n=(n=n+Math.imul(v,tt)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,tt)|0,i=i+Math.imul(p,rt)|0,n=(n=n+Math.imul(p,it)|0)+Math.imul(m,rt)|0,o=o+Math.imul(m,it)|0;var _t=(u+(i=i+Math.imul(l,ot)|0)|0)+((8191&(n=(n=n+Math.imul(l,st)|0)+Math.imul(d,ot)|0))<<13)|0;u=((o=o+Math.imul(d,st)|0)+(n>>>13)|0)+(_t>>>26)|0,_t&=67108863,i=Math.imul(C,q),n=(n=Math.imul(C,F))+Math.imul(P,q)|0,o=Math.imul(P,F),i=i+Math.imul(T,H)|0,n=(n=n+Math.imul(T,K)|0)+Math.imul(x,H)|0,o=o+Math.imul(x,K)|0,i=i+Math.imul(k,G)|0,n=(n=n+Math.imul(k,Y)|0)+Math.imul(B,G)|0,o=o+Math.imul(B,Y)|0,i=i+Math.imul(A,$)|0,n=(n=n+Math.imul(A,X)|0)+Math.imul(E,$)|0,o=o+Math.imul(E,X)|0,i=i+Math.imul(w,Q)|0,n=(n=n+Math.imul(w,tt)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,tt)|0,i=i+Math.imul(v,rt)|0,n=(n=n+Math.imul(v,it)|0)+Math.imul(b,rt)|0,o=o+Math.imul(b,it)|0,i=i+Math.imul(p,ot)|0,n=(n=n+Math.imul(p,st)|0)+Math.imul(m,ot)|0,o=o+Math.imul(m,st)|0;var At=(u+(i=i+Math.imul(l,ht)|0)|0)+((8191&(n=(n=n+Math.imul(l,ut)|0)+Math.imul(d,ht)|0))<<13)|0;u=((o=o+Math.imul(d,ut)|0)+(n>>>13)|0)+(At>>>26)|0,At&=67108863,i=Math.imul(L,q),n=(n=Math.imul(L,F))+Math.imul(O,q)|0,o=Math.imul(O,F),i=i+Math.imul(C,H)|0,n=(n=n+Math.imul(C,K)|0)+Math.imul(P,H)|0,o=o+Math.imul(P,K)|0,i=i+Math.imul(T,G)|0,n=(n=n+Math.imul(T,Y)|0)+Math.imul(x,G)|0,o=o+Math.imul(x,Y)|0,i=i+Math.imul(k,$)|0,n=(n=n+Math.imul(k,X)|0)+Math.imul(B,$)|0,o=o+Math.imul(B,X)|0,i=i+Math.imul(A,Q)|0,n=(n=n+Math.imul(A,tt)|0)+Math.imul(E,Q)|0,o=o+Math.imul(E,tt)|0,i=i+Math.imul(w,rt)|0,n=(n=n+Math.imul(w,it)|0)+Math.imul(M,rt)|0,o=o+Math.imul(M,it)|0,i=i+Math.imul(v,ot)|0,n=(n=n+Math.imul(v,st)|0)+Math.imul(b,ot)|0,o=o+Math.imul(b,st)|0,i=i+Math.imul(p,ht)|0,n=(n=n+Math.imul(p,ut)|0)+Math.imul(m,ht)|0,o=o+Math.imul(m,ut)|0;var Et=(u+(i=i+Math.imul(l,lt)|0)|0)+((8191&(n=(n=n+Math.imul(l,dt)|0)+Math.imul(d,lt)|0))<<13)|0;u=((o=o+Math.imul(d,dt)|0)+(n>>>13)|0)+(Et>>>26)|0,Et&=67108863,i=Math.imul(U,q),n=(n=Math.imul(U,F))+Math.imul(N,q)|0,o=Math.imul(N,F),i=i+Math.imul(L,H)|0,n=(n=n+Math.imul(L,K)|0)+Math.imul(O,H)|0,o=o+Math.imul(O,K)|0,i=i+Math.imul(C,G)|0,n=(n=n+Math.imul(C,Y)|0)+Math.imul(P,G)|0,o=o+Math.imul(P,Y)|0,i=i+Math.imul(T,$)|0,n=(n=n+Math.imul(T,X)|0)+Math.imul(x,$)|0,o=o+Math.imul(x,X)|0,i=i+Math.imul(k,Q)|0,n=(n=n+Math.imul(k,tt)|0)+Math.imul(B,Q)|0,o=o+Math.imul(B,tt)|0,i=i+Math.imul(A,rt)|0,n=(n=n+Math.imul(A,it)|0)+Math.imul(E,rt)|0,o=o+Math.imul(E,it)|0,i=i+Math.imul(w,ot)|0,n=(n=n+Math.imul(w,st)|0)+Math.imul(M,ot)|0,o=o+Math.imul(M,st)|0,i=i+Math.imul(v,ht)|0,n=(n=n+Math.imul(v,ut)|0)+Math.imul(b,ht)|0,o=o+Math.imul(b,ut)|0,i=i+Math.imul(p,lt)|0,n=(n=n+Math.imul(p,dt)|0)+Math.imul(m,lt)|0,o=o+Math.imul(m,dt)|0;var St=(u+(i=i+Math.imul(l,pt)|0)|0)+((8191&(n=(n=n+Math.imul(l,mt)|0)+Math.imul(d,pt)|0))<<13)|0;u=((o=o+Math.imul(d,mt)|0)+(n>>>13)|0)+(St>>>26)|0,St&=67108863,i=Math.imul(U,H),n=(n=Math.imul(U,K))+Math.imul(N,H)|0,o=Math.imul(N,K),i=i+Math.imul(L,G)|0,n=(n=n+Math.imul(L,Y)|0)+Math.imul(O,G)|0,o=o+Math.imul(O,Y)|0,i=i+Math.imul(C,$)|0,n=(n=n+Math.imul(C,X)|0)+Math.imul(P,$)|0,o=o+Math.imul(P,X)|0,i=i+Math.imul(T,Q)|0,n=(n=n+Math.imul(T,tt)|0)+Math.imul(x,Q)|0,o=o+Math.imul(x,tt)|0,i=i+Math.imul(k,rt)|0,n=(n=n+Math.imul(k,it)|0)+Math.imul(B,rt)|0,o=o+Math.imul(B,it)|0,i=i+Math.imul(A,ot)|0,n=(n=n+Math.imul(A,st)|0)+Math.imul(E,ot)|0,o=o+Math.imul(E,st)|0,i=i+Math.imul(w,ht)|0,n=(n=n+Math.imul(w,ut)|0)+Math.imul(M,ht)|0,o=o+Math.imul(M,ut)|0,i=i+Math.imul(v,lt)|0,n=(n=n+Math.imul(v,dt)|0)+Math.imul(b,lt)|0,o=o+Math.imul(b,dt)|0;var kt=(u+(i=i+Math.imul(p,pt)|0)|0)+((8191&(n=(n=n+Math.imul(p,mt)|0)+Math.imul(m,pt)|0))<<13)|0;u=((o=o+Math.imul(m,mt)|0)+(n>>>13)|0)+(kt>>>26)|0,kt&=67108863,i=Math.imul(U,G),n=(n=Math.imul(U,Y))+Math.imul(N,G)|0,o=Math.imul(N,Y),i=i+Math.imul(L,$)|0,n=(n=n+Math.imul(L,X)|0)+Math.imul(O,$)|0,o=o+Math.imul(O,X)|0,i=i+Math.imul(C,Q)|0,n=(n=n+Math.imul(C,tt)|0)+Math.imul(P,Q)|0,o=o+Math.imul(P,tt)|0,i=i+Math.imul(T,rt)|0,n=(n=n+Math.imul(T,it)|0)+Math.imul(x,rt)|0,o=o+Math.imul(x,it)|0,i=i+Math.imul(k,ot)|0,n=(n=n+Math.imul(k,st)|0)+Math.imul(B,ot)|0,o=o+Math.imul(B,st)|0,i=i+Math.imul(A,ht)|0,n=(n=n+Math.imul(A,ut)|0)+Math.imul(E,ht)|0,o=o+Math.imul(E,ut)|0,i=i+Math.imul(w,lt)|0,n=(n=n+Math.imul(w,dt)|0)+Math.imul(M,lt)|0,o=o+Math.imul(M,dt)|0;var Bt=(u+(i=i+Math.imul(v,pt)|0)|0)+((8191&(n=(n=n+Math.imul(v,mt)|0)+Math.imul(b,pt)|0))<<13)|0;u=((o=o+Math.imul(b,mt)|0)+(n>>>13)|0)+(Bt>>>26)|0,Bt&=67108863,i=Math.imul(U,$),n=(n=Math.imul(U,X))+Math.imul(N,$)|0,o=Math.imul(N,X),i=i+Math.imul(L,Q)|0,n=(n=n+Math.imul(L,tt)|0)+Math.imul(O,Q)|0,o=o+Math.imul(O,tt)|0,i=i+Math.imul(C,rt)|0,n=(n=n+Math.imul(C,it)|0)+Math.imul(P,rt)|0,o=o+Math.imul(P,it)|0,i=i+Math.imul(T,ot)|0,n=(n=n+Math.imul(T,st)|0)+Math.imul(x,ot)|0,o=o+Math.imul(x,st)|0,i=i+Math.imul(k,ht)|0,n=(n=n+Math.imul(k,ut)|0)+Math.imul(B,ht)|0,o=o+Math.imul(B,ut)|0,i=i+Math.imul(A,lt)|0,n=(n=n+Math.imul(A,dt)|0)+Math.imul(E,lt)|0,o=o+Math.imul(E,dt)|0;var Rt=(u+(i=i+Math.imul(w,pt)|0)|0)+((8191&(n=(n=n+Math.imul(w,mt)|0)+Math.imul(M,pt)|0))<<13)|0;u=((o=o+Math.imul(M,mt)|0)+(n>>>13)|0)+(Rt>>>26)|0,Rt&=67108863,i=Math.imul(U,Q),n=(n=Math.imul(U,tt))+Math.imul(N,Q)|0,o=Math.imul(N,tt),i=i+Math.imul(L,rt)|0,n=(n=n+Math.imul(L,it)|0)+Math.imul(O,rt)|0,o=o+Math.imul(O,it)|0,i=i+Math.imul(C,ot)|0,n=(n=n+Math.imul(C,st)|0)+Math.imul(P,ot)|0,o=o+Math.imul(P,st)|0,i=i+Math.imul(T,ht)|0,n=(n=n+Math.imul(T,ut)|0)+Math.imul(x,ht)|0,o=o+Math.imul(x,ut)|0,i=i+Math.imul(k,lt)|0,n=(n=n+Math.imul(k,dt)|0)+Math.imul(B,lt)|0,o=o+Math.imul(B,dt)|0;var Tt=(u+(i=i+Math.imul(A,pt)|0)|0)+((8191&(n=(n=n+Math.imul(A,mt)|0)+Math.imul(E,pt)|0))<<13)|0;u=((o=o+Math.imul(E,mt)|0)+(n>>>13)|0)+(Tt>>>26)|0,Tt&=67108863,i=Math.imul(U,rt),n=(n=Math.imul(U,it))+Math.imul(N,rt)|0,o=Math.imul(N,it),i=i+Math.imul(L,ot)|0,n=(n=n+Math.imul(L,st)|0)+Math.imul(O,ot)|0,o=o+Math.imul(O,st)|0,i=i+Math.imul(C,ht)|0,n=(n=n+Math.imul(C,ut)|0)+Math.imul(P,ht)|0,o=o+Math.imul(P,ut)|0,i=i+Math.imul(T,lt)|0,n=(n=n+Math.imul(T,dt)|0)+Math.imul(x,lt)|0,o=o+Math.imul(x,dt)|0;var xt=(u+(i=i+Math.imul(k,pt)|0)|0)+((8191&(n=(n=n+Math.imul(k,mt)|0)+Math.imul(B,pt)|0))<<13)|0;u=((o=o+Math.imul(B,mt)|0)+(n>>>13)|0)+(xt>>>26)|0,xt&=67108863,i=Math.imul(U,ot),n=(n=Math.imul(U,st))+Math.imul(N,ot)|0,o=Math.imul(N,st),i=i+Math.imul(L,ht)|0,n=(n=n+Math.imul(L,ut)|0)+Math.imul(O,ht)|0,o=o+Math.imul(O,ut)|0,i=i+Math.imul(C,lt)|0,n=(n=n+Math.imul(C,dt)|0)+Math.imul(P,lt)|0,o=o+Math.imul(P,dt)|0;var It=(u+(i=i+Math.imul(T,pt)|0)|0)+((8191&(n=(n=n+Math.imul(T,mt)|0)+Math.imul(x,pt)|0))<<13)|0;u=((o=o+Math.imul(x,mt)|0)+(n>>>13)|0)+(It>>>26)|0,It&=67108863,i=Math.imul(U,ht),n=(n=Math.imul(U,ut))+Math.imul(N,ht)|0,o=Math.imul(N,ut),i=i+Math.imul(L,lt)|0,n=(n=n+Math.imul(L,dt)|0)+Math.imul(O,lt)|0,o=o+Math.imul(O,dt)|0;var Ct=(u+(i=i+Math.imul(C,pt)|0)|0)+((8191&(n=(n=n+Math.imul(C,mt)|0)+Math.imul(P,pt)|0))<<13)|0;u=((o=o+Math.imul(P,mt)|0)+(n>>>13)|0)+(Ct>>>26)|0,Ct&=67108863,i=Math.imul(U,lt),n=(n=Math.imul(U,dt))+Math.imul(N,lt)|0,o=Math.imul(N,dt);var Pt=(u+(i=i+Math.imul(L,pt)|0)|0)+((8191&(n=(n=n+Math.imul(L,mt)|0)+Math.imul(O,pt)|0))<<13)|0;u=((o=o+Math.imul(O,mt)|0)+(n>>>13)|0)+(Pt>>>26)|0,Pt&=67108863;var Dt=(u+(i=Math.imul(U,pt))|0)+((8191&(n=(n=Math.imul(U,mt))+Math.imul(N,pt)|0))<<13)|0;return u=((o=Math.imul(N,mt))+(n>>>13)|0)+(Dt>>>26)|0,Dt&=67108863,h[0]=gt,h[1]=vt,h[2]=bt,h[3]=yt,h[4]=wt,h[5]=Mt,h[6]=_t,h[7]=At,h[8]=Et,h[9]=St,h[10]=kt,h[11]=Bt,h[12]=Rt,h[13]=Tt,h[14]=xt,h[15]=It,h[16]=Ct,h[17]=Pt,h[18]=Dt,0!==u&&(h[19]=u,r.length++),r};function g(t,e,r){r.negative=e.negative^t.negative,r.length=t.length+e.length;for(var i=0,n=0,o=0;o>>26)|0)>>>26,s&=67108863}r.words[o]=a,i=s,s=n}return 0!==i?r.words[o]=i:r.length--,r._strip()}function v(t,e,r){return g(t,e,r)}function b(t,e){this.x=t,this.y=e}Math.imul||(m=p),n.prototype.mulTo=function(t,e){var r=this.length+t.length;return 10===this.length&&10===t.length?m(this,t,e):r<63?p(this,t,e):r<1024?g(this,t,e):v(this,t,e)},b.prototype.makeRBT=function(t){for(var e=new Array(t),r=n.prototype._countBits(t)-1,i=0;i>=1;return i},b.prototype.permute=function(t,e,r,i,n,o){for(var s=0;s>>=1)n++;return 1<>>=13,i[2*s+1]=8191&o,o>>>=13;for(s=2*e;s>=26,i+=o/67108864|0,i+=s>>>26,this.words[n]=67108863&s}return 0!==i&&(this.words[n]=i,this.length++),e?this.ineg():this},n.prototype.muln=function(t){return this.clone().imuln(t)},n.prototype.sqr=function(){return this.mul(this)},n.prototype.isqr=function(){return this.imul(this.clone())},n.prototype.pow=function(t){var e=function(t){for(var e=new Array(t.bitLength()),r=0;r>>n&1}return e}(t);if(0===e.length)return new n(1);for(var r=this,i=0;i=0);var e,i=t%26,n=(t-i)/26,o=67108863>>>26-i<<26-i;if(0!==i){var s=0;for(e=0;e>>26-i}s&&(this.words[e]=s,this.length++)}if(0!==n){for(e=this.length-1;e>=0;e--)this.words[e+n]=this.words[e];for(e=0;e=0),n=e?(e-e%26)/26:0;var o=t%26,s=Math.min((t-o)/26,this.length),a=67108863^67108863>>>o<s)for(this.length-=s,u=0;u=0&&(0!==f||u>=n);u--){var l=0|this.words[u];this.words[u]=f<<26-o|l>>>o,f=l&a}return h&&0!==f&&(h.words[h.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this._strip()},n.prototype.ishrn=function(t,e,i){return r(0===this.negative),this.iushrn(t,e,i)},n.prototype.shln=function(t){return this.clone().ishln(t)},n.prototype.ushln=function(t){return this.clone().iushln(t)},n.prototype.shrn=function(t){return this.clone().ishrn(t)},n.prototype.ushrn=function(t){return this.clone().iushrn(t)},n.prototype.testn=function(t){r("number"==typeof t&&t>=0);var e=t%26,i=(t-e)/26,n=1<=0);var e=t%26,i=(t-e)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=i)return this;if(0!==e&&i++,this.length=Math.min(i,this.length),0!==e){var n=67108863^67108863>>>e<=67108864;e++)this.words[e]-=67108864,e===this.length-1?this.words[e+1]=1:this.words[e+1]++;return this.length=Math.max(this.length,e+1),this},n.prototype.isubn=function(t){if(r("number"==typeof t),r(t<67108864),t<0)return this.iaddn(-t);if(0!==this.negative)return this.negative=0,this.iaddn(t),this.negative=1,this;if(this.words[0]-=t,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var e=0;e>26)-(h/67108864|0),this.words[n+i]=67108863&o}for(;n>26,this.words[n+i]=67108863&o;if(0===a)return this._strip();for(r(-1===a),a=0,n=0;n>26,this.words[n]=67108863&o;return this.negative=1,this._strip()},n.prototype._wordDiv=function(t,e){var r=(this.length,t.length),i=this.clone(),o=t,s=0|o.words[o.length-1];0!==(r=26-this._countBits(s))&&(o=o.ushln(r),i.iushln(r),s=0|o.words[o.length-1]);var a,h=i.length-o.length;if("mod"!==e){(a=new n(null)).length=h+1,a.words=new Array(a.length);for(var u=0;u=0;l--){var d=67108864*(0|i.words[o.length+l])+(0|i.words[o.length+l-1]);for(d=Math.min(d/s|0,67108863),i._ishlnsubmul(o,d,l);0!==i.negative;)d--,i.negative=0,i._ishlnsubmul(o,1,l),i.isZero()||(i.negative^=1);a&&(a.words[l]=d)}return a&&a._strip(),i._strip(),"div"!==e&&0!==r&&i.iushrn(r),{div:a||null,mod:i}},n.prototype.divmod=function(t,e,i){return r(!t.isZero()),this.isZero()?{div:new n(0),mod:new n(0)}:0!==this.negative&&0===t.negative?(a=this.neg().divmod(t,e),"mod"!==e&&(o=a.div.neg()),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.iadd(t)),{div:o,mod:s}):0===this.negative&&0!==t.negative?(a=this.divmod(t.neg(),e),"mod"!==e&&(o=a.div.neg()),{div:o,mod:a.mod}):0!=(this.negative&t.negative)?(a=this.neg().divmod(t.neg(),e),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.isub(t)),{div:a.div,mod:s}):t.length>this.length||this.cmp(t)<0?{div:new n(0),mod:this}:1===t.length?"div"===e?{div:this.divn(t.words[0]),mod:null}:"mod"===e?{div:null,mod:new n(this.modrn(t.words[0]))}:{div:this.divn(t.words[0]),mod:new n(this.modrn(t.words[0]))}:this._wordDiv(t,e);var o,s,a},n.prototype.div=function(t){return this.divmod(t,"div",!1).div},n.prototype.mod=function(t){return this.divmod(t,"mod",!1).mod},n.prototype.umod=function(t){return this.divmod(t,"mod",!0).mod},n.prototype.divRound=function(t){var e=this.divmod(t);if(e.mod.isZero())return e.div;var r=0!==e.div.negative?e.mod.isub(t):e.mod,i=t.ushrn(1),n=t.andln(1),o=r.cmp(i);return o<0||1===n&&0===o?e.div:0!==e.div.negative?e.div.isubn(1):e.div.iaddn(1)},n.prototype.modrn=function(t){var e=t<0;e&&(t=-t),r(t<=67108863);for(var i=(1<<26)%t,n=0,o=this.length-1;o>=0;o--)n=(i*n+(0|this.words[o]))%t;return e?-n:n},n.prototype.modn=function(t){return this.modrn(t)},n.prototype.idivn=function(t){var e=t<0;e&&(t=-t),r(t<=67108863);for(var i=0,n=this.length-1;n>=0;n--){var o=(0|this.words[n])+67108864*i;this.words[n]=o/t|0,i=o%t}return this._strip(),e?this.ineg():this},n.prototype.divn=function(t){return this.clone().idivn(t)},n.prototype.egcd=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o=new n(1),s=new n(0),a=new n(0),h=new n(1),u=0;e.isEven()&&i.isEven();)e.iushrn(1),i.iushrn(1),++u;for(var f=i.clone(),l=e.clone();!e.isZero();){for(var d=0,c=1;0==(e.words[0]&c)&&d<26;++d,c<<=1);if(d>0)for(e.iushrn(d);d-- >0;)(o.isOdd()||s.isOdd())&&(o.iadd(f),s.isub(l)),o.iushrn(1),s.iushrn(1);for(var p=0,m=1;0==(i.words[0]&m)&&p<26;++p,m<<=1);if(p>0)for(i.iushrn(p);p-- >0;)(a.isOdd()||h.isOdd())&&(a.iadd(f),h.isub(l)),a.iushrn(1),h.iushrn(1);e.cmp(i)>=0?(e.isub(i),o.isub(a),s.isub(h)):(i.isub(e),a.isub(o),h.isub(s))}return{a:a,b:h,gcd:i.iushln(u)}},n.prototype._invmp=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o,s=new n(1),a=new n(0),h=i.clone();e.cmpn(1)>0&&i.cmpn(1)>0;){for(var u=0,f=1;0==(e.words[0]&f)&&u<26;++u,f<<=1);if(u>0)for(e.iushrn(u);u-- >0;)s.isOdd()&&s.iadd(h),s.iushrn(1);for(var l=0,d=1;0==(i.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(i.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(h),a.iushrn(1);e.cmp(i)>=0?(e.isub(i),s.isub(a)):(i.isub(e),a.isub(s))}return(o=0===e.cmpn(1)?s:a).cmpn(0)<0&&o.iadd(t),o},n.prototype.gcd=function(t){if(this.isZero())return t.abs();if(t.isZero())return this.abs();var e=this.clone(),r=t.clone();e.negative=0,r.negative=0;for(var i=0;e.isEven()&&r.isEven();i++)e.iushrn(1),r.iushrn(1);for(;;){for(;e.isEven();)e.iushrn(1);for(;r.isEven();)r.iushrn(1);var n=e.cmp(r);if(n<0){var o=e;e=r,r=o}else if(0===n||0===r.cmpn(1))break;e.isub(r)}return r.iushln(i)},n.prototype.invm=function(t){return this.egcd(t).a.umod(t)},n.prototype.isEven=function(){return 0==(1&this.words[0])},n.prototype.isOdd=function(){return 1==(1&this.words[0])},n.prototype.andln=function(t){return this.words[0]&t},n.prototype.bincn=function(t){r("number"==typeof t);var e=t%26,i=(t-e)/26,n=1<>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},n.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},n.prototype.cmpn=function(t){var e,i=t<0;if(0!==this.negative&&!i)return-1;if(0===this.negative&&i)return 1;if(this._strip(),this.length>1)e=1;else{i&&(t=-t),r(t<=67108863,"Number is too big");var n=0|this.words[0];e=n===t?0:nt.length)return 1;if(this.length=0;r--){var i=0|this.words[r],n=0|t.words[r];if(i!==n){in&&(e=1);break}}return e},n.prototype.gtn=function(t){return 1===this.cmpn(t)},n.prototype.gt=function(t){return 1===this.cmp(t)},n.prototype.gten=function(t){return this.cmpn(t)>=0},n.prototype.gte=function(t){return this.cmp(t)>=0},n.prototype.ltn=function(t){return-1===this.cmpn(t)},n.prototype.lt=function(t){return-1===this.cmp(t)},n.prototype.lten=function(t){return this.cmpn(t)<=0},n.prototype.lte=function(t){return this.cmp(t)<=0},n.prototype.eqn=function(t){return 0===this.cmpn(t)},n.prototype.eq=function(t){return 0===this.cmp(t)},n.red=function(t){return new S(t)},n.prototype.toRed=function(t){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),t.convertTo(this)._forceRed(t)},n.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},n.prototype._forceRed=function(t){return this.red=t,this},n.prototype.forceRed=function(t){return r(!this.red,"Already a number in reduction context"),this._forceRed(t)},n.prototype.redAdd=function(t){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,t)},n.prototype.redIAdd=function(t){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,t)},n.prototype.redSub=function(t){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,t)},n.prototype.redISub=function(t){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,t)},n.prototype.redShl=function(t){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,t)},n.prototype.redMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.mul(this,t)},n.prototype.redIMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.imul(this,t)},n.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},n.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},n.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},n.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},n.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},n.prototype.redPow=function(t){return r(this.red&&!t.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,t)};var y={k256:null,p224:null,p192:null,p25519:null};function w(t,e){this.name=t,this.p=new n(e,16),this.n=this.p.bitLength(),this.k=new n(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function M(){w.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function _(){w.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function A(){w.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function E(){w.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function S(t){if("string"==typeof t){var e=n._prime(t);this.m=e.p,this.prime=e}else r(t.gtn(1),"modulus must be greater than 1"),this.m=t,this.prime=null}function k(t){S.call(this,t),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new n(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}w.prototype._tmp=function(){var t=new n(null);return t.words=new Array(Math.ceil(this.n/13)),t},w.prototype.ireduce=function(t){var e,r=t;do{this.split(r,this.tmp),e=(r=(r=this.imulK(r)).iadd(this.tmp)).bitLength()}while(e>this.n);var i=e0?r.isub(this.p):void 0!==r.strip?r.strip():r._strip(),r},w.prototype.split=function(t,e){t.iushrn(this.n,0,e)},w.prototype.imulK=function(t){return t.imul(this.k)},i(M,w),M.prototype.split=function(t,e){for(var r=4194303,i=Math.min(t.length,9),n=0;n>>22,o=s}o>>>=22,t.words[n-10]=o,0===o&&t.length>10?t.length-=10:t.length-=9},M.prototype.imulK=function(t){t.words[t.length]=0,t.words[t.length+1]=0,t.length+=2;for(var e=0,r=0;r>>=26,t.words[r]=n,e=i}return 0!==e&&(t.words[t.length++]=e),t},n._prime=function(t){if(y[t])return y[t];var e;if("k256"===t)e=new M;else if("p224"===t)e=new _;else if("p192"===t)e=new A;else{if("p25519"!==t)throw new Error("Unknown prime "+t);e=new E}return y[t]=e,e},S.prototype._verify1=function(t){r(0===t.negative,"red works only with positives"),r(t.red,"red works only with red numbers")},S.prototype._verify2=function(t,e){r(0==(t.negative|e.negative),"red works only with positives"),r(t.red&&t.red===e.red,"red works only with red numbers")},S.prototype.imod=function(t){return this.prime?this.prime.ireduce(t)._forceRed(this):(u(t,t.umod(this.m)._forceRed(this)),t)},S.prototype.neg=function(t){return t.isZero()?t.clone():this.m.sub(t)._forceRed(this)},S.prototype.add=function(t,e){this._verify2(t,e);var r=t.add(e);return r.cmp(this.m)>=0&&r.isub(this.m),r._forceRed(this)},S.prototype.iadd=function(t,e){this._verify2(t,e);var r=t.iadd(e);return r.cmp(this.m)>=0&&r.isub(this.m),r},S.prototype.sub=function(t,e){this._verify2(t,e);var r=t.sub(e);return r.cmpn(0)<0&&r.iadd(this.m),r._forceRed(this)},S.prototype.isub=function(t,e){this._verify2(t,e);var r=t.isub(e);return r.cmpn(0)<0&&r.iadd(this.m),r},S.prototype.shl=function(t,e){return this._verify1(t),this.imod(t.ushln(e))},S.prototype.imul=function(t,e){return this._verify2(t,e),this.imod(t.imul(e))},S.prototype.mul=function(t,e){return this._verify2(t,e),this.imod(t.mul(e))},S.prototype.isqr=function(t){return this.imul(t,t.clone())},S.prototype.sqr=function(t){return this.mul(t,t)},S.prototype.sqrt=function(t){if(t.isZero())return t.clone();var e=this.m.andln(3);if(r(e%2==1),3===e){var i=this.m.add(new n(1)).iushrn(2);return this.pow(t,i)}for(var o=this.m.subn(1),s=0;!o.isZero()&&0===o.andln(1);)s++,o.iushrn(1);r(!o.isZero());var a=new n(1).toRed(this),h=a.redNeg(),u=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new n(2*f*f).toRed(this);0!==this.pow(f,u).cmp(h);)f.redIAdd(h);for(var l=this.pow(f,o),d=this.pow(t,o.addn(1).iushrn(1)),c=this.pow(t,o),p=s;0!==c.cmp(a);){for(var m=c,g=0;0!==m.cmp(a);g++)m=m.redSqr();r(g=0;i--){for(var u=e.words[i],f=h-1;f>=0;f--){var l=u>>f&1;o!==r[0]&&(o=this.sqr(o)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===i&&0===f)&&(o=this.mul(o,r[s]),a=0,s=0)):a=0}h=26}return o},S.prototype.convertTo=function(t){var e=t.umod(this.m);return e===t?e.clone():e},S.prototype.convertFrom=function(t){var e=t.clone();return e.red=null,e},n.mont=function(t){return new k(t)},i(k,S),k.prototype.convertTo=function(t){return this.imod(t.ushln(this.shift))},k.prototype.convertFrom=function(t){var e=this.imod(t.mul(this.rinv));return e.red=null,e},k.prototype.imul=function(t,e){if(t.isZero()||e.isZero())return t.words[0]=0,t.length=1,t;var r=t.imul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),n=r.isub(i).iushrn(this.shift),o=n;return n.cmp(this.m)>=0?o=n.isub(this.m):n.cmpn(0)<0&&(o=n.iadd(this.m)),o._forceRed(this)},k.prototype.mul=function(t,e){if(t.isZero()||e.isZero())return new n(0)._forceRed(this);var r=t.mul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),o=r.isub(i).iushrn(this.shift),s=o;return o.cmp(this.m)>=0?s=o.isub(this.m):o.cmpn(0)<0&&(s=o.iadd(this.m)),s._forceRed(this)},k.prototype.invm=function(t){return this.imod(t._invmp(this.m).mul(this.r2))._forceRed(this)}}(void 0===t||t,this),t.exports}.call({})}function Dg(){return Cg||(Cg=!0,Pg()),Ig}var Lg,Og,jg,Ug,Ng=!1;function zg(t){var e,r=t.modulus.byteLength();do{e=new jg(Ug(r))}while(e.cmp(t.modulus)>=0||!e.umod(t.prime1)||!e.umod(t.prime2));return e}function qg(t,e){var r=function(t){var e=zg(t);return{blinder:e.toRed(jg.mont(t.modulus)).redPow(new jg(t.publicExponent)).fromRed(),unblinder:e.invm(t.modulus)}}(e),i=e.modulus.byteLength(),n=new jg(t).mul(r.blinder).umod(e.modulus),o=n.toRed(jg.mont(e.prime1)),s=n.toRed(jg.mont(e.prime2)),a=e.coefficient,h=e.prime1,u=e.prime2,f=o.redPow(e.exponent1).fromRed(),l=s.redPow(e.exponent2).fromRed(),d=f.isub(l).imul(a).umod(h).imul(u);return l.iadd(d).imul(r.unblinder).umod(e.modulus).toArrayLike(Og,"be",i)}function Fg(){return Ng||(Ng=!0,Lg={},Og=Ye().Buffer,jg=Dg(),Ug=wr(),qg.getr=zg,Lg=qg),Lg}var Zg,Hg=!1;function Kg(){return Hg||(Hg=!0,Zg={},Zg=JSON.parse('{"_from":"elliptic@^6.5.3","_id":"elliptic@6.5.4","_inBundle":false,"_integrity":"sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==","_location":"/elliptic","_phantomChildren":{},"_requested":{"type":"range","registry":true,"raw":"elliptic@^6.5.3","name":"elliptic","escapedName":"elliptic","rawSpec":"^6.5.3","saveSpec":null,"fetchSpec":"^6.5.3"},"_requiredBy":["/browserify-sign","/create-ecdh"],"_resolved":"https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz","_shasum":"da37cebd31e79a1367e941b592ed1fbebd58abbb","_spec":"elliptic@^6.5.3","_where":"/home/lin/server/jessibuca/node_modules/browserify-sign","author":{"name":"Fedor Indutny","email":"fedor@indutny.com"},"bugs":{"url":"https://github.com/indutny/elliptic/issues"},"bundleDependencies":false,"dependencies":{"bn.js":"^4.11.9","brorand":"^1.1.0","hash.js":"^1.0.0","hmac-drbg":"^1.0.1","inherits":"^2.0.4","minimalistic-assert":"^1.0.1","minimalistic-crypto-utils":"^1.0.1"},"deprecated":false,"description":"EC cryptography","devDependencies":{"brfs":"^2.0.2","coveralls":"^3.1.0","eslint":"^7.6.0","grunt":"^1.2.1","grunt-browserify":"^5.3.0","grunt-cli":"^1.3.2","grunt-contrib-connect":"^3.0.0","grunt-contrib-copy":"^1.0.0","grunt-contrib-uglify":"^5.0.0","grunt-mocha-istanbul":"^5.0.2","grunt-saucelabs":"^9.0.1","istanbul":"^0.4.5","mocha":"^8.0.1"},"files":["lib"],"homepage":"https://github.com/indutny/elliptic","keywords":["EC","Elliptic","curve","Cryptography"],"license":"MIT","main":"lib/elliptic.js","name":"elliptic","repository":{"type":"git","url":"git+ssh://git@github.com/indutny/elliptic.git"},"scripts":{"lint":"eslint lib test","lint:fix":"npm run lint -- --fix","test":"npm run lint && npm run unit","unit":"istanbul test _mocha --reporter=spec test/index.js","version":"grunt dist && git add dist/"},"version":"6.5.4"}')),Zg}var Wg,Gg=!1;function Yg(){Wg=function(){var t={exports:this};return function(t,e){function r(t,e){if(!t)throw new Error(e||"Assertion failed")}function i(t,e){t.super_=e;var r=function(){};r.prototype=e.prototype,t.prototype=new r,t.prototype.constructor=t}function n(t,e,r){if(n.isBN(t))return t;this.negative=0,this.words=null,this.length=0,this.red=null,null!==t&&("le"!==e&&"be"!==e||(r=e,e=10),this._init(t||0,e||10,r||"be"))}var o;"object"==typeof t?t.exports=n:e.BN=n,n.BN=n,n.wordSize=26;try{o="undefined"!=typeof window&&void 0!==window.Buffer?window.Buffer:ir().Buffer}catch(t){}function s(t,e){var r=t.charCodeAt(e);return r>=65&&r<=70?r-55:r>=97&&r<=102?r-87:r-48&15}function a(t,e,r){var i=s(t,r);return r-1>=e&&(i|=s(t,r-1)<<4),i}function h(t,e,r,i){for(var n=0,o=Math.min(t.length,r),s=e;s=49?a-49+10:a>=17?a-17+10:a}return n}n.isBN=function(t){return t instanceof n||null!==t&&"object"==typeof t&&t.constructor.wordSize===n.wordSize&&Array.isArray(t.words)},n.max=function(t,e){return t.cmp(e)>0?t:e},n.min=function(t,e){return t.cmp(e)<0?t:e},n.prototype._init=function(t,e,i){if("number"==typeof t)return this._initNumber(t,e,i);if("object"==typeof t)return this._initArray(t,e,i);"hex"===e&&(e=16),r(e===(0|e)&&e>=2&&e<=36);var n=0;"-"===(t=t.toString().replace(/\s+/g,""))[0]&&(n++,this.negative=1),n=0;n-=3)s=t[n]|t[n-1]<<8|t[n-2]<<16,this.words[o]|=s<>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===i)for(n=0,o=0;n>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this.strip()},n.prototype._parseHex=function(t,e,r){this.length=Math.ceil((t.length-e)/6),this.words=new Array(this.length);for(var i=0;i=e;i-=2)n=a(t,e,i)<=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;else for(i=(t.length-e)%2==0?e+1:e;i=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;this.strip()},n.prototype._parseBase=function(t,e,r){this.words=[0],this.length=1;for(var i=0,n=1;n<=67108863;n*=e)i++;i--,n=n/e|0;for(var o=t.length-r,s=o%i,a=Math.min(o,o-s)+r,u=0,f=r;f1&&0===this.words[this.length-1];)this.length--;return this._normSign()},n.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},n.prototype.inspect=function(){return(this.red?""};var u=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],f=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],l=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];function d(t,e,r){r.negative=e.negative^t.negative;var i=t.length+e.length|0;r.length=i,i=i-1|0;var n=0|t.words[0],o=0|e.words[0],s=n*o,a=67108863&s,h=s/67108864|0;r.words[0]=a;for(var u=1;u>>26,l=67108863&h,d=Math.min(u,e.length-1),c=Math.max(0,u-t.length+1);c<=d;c++){var p=u-c|0;f+=(s=(n=0|t.words[p])*(o=0|e.words[c])+l)/67108864|0,l=67108863&s}r.words[u]=0|l,h=0|f}return 0!==h?r.words[u]=0|h:r.length--,r.strip()}n.prototype.toString=function(t,e){var i;if(e=0|e||1,16===(t=t||10)||"hex"===t){i="";for(var n=0,o=0,s=0;s>>24-n&16777215)||s!==this.length-1?u[6-h.length]+h+i:h+i,(n+=2)>=26&&(n-=26,s--)}for(0!==o&&(i=o.toString(16)+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}if(t===(0|t)&&t>=2&&t<=36){var d=f[t],c=l[t];i="";var p=this.clone();for(p.negative=0;!p.isZero();){var m=p.modn(c).toString(t);i=(p=p.idivn(c)).isZero()?m+i:u[d-m.length]+m+i}for(this.isZero()&&(i="0"+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}r(!1,"Base should be between 2 and 36")},n.prototype.toNumber=function(){var t=this.words[0];return 2===this.length?t+=67108864*this.words[1]:3===this.length&&1===this.words[2]?t+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-t:t},n.prototype.toJSON=function(){return this.toString(16)},n.prototype.toBuffer=function(t,e){return r(void 0!==o),this.toArrayLike(o,t,e)},n.prototype.toArray=function(t,e){return this.toArrayLike(Array,t,e)},n.prototype.toArrayLike=function(t,e,i){var n=this.byteLength(),o=i||Math.max(1,n);r(n<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0"),this.strip();var s,a,h="le"===e,u=new t(o),f=this.clone();if(h){for(a=0;!f.isZero();a++)s=f.andln(255),f.iushrn(8),u[a]=s;for(;a=4096&&(r+=13,e>>>=13),e>=64&&(r+=7,e>>>=7),e>=8&&(r+=4,e>>>=4),e>=2&&(r+=2,e>>>=2),r+e},n.prototype._zeroBits=function(t){if(0===t)return 26;var e=t,r=0;return 0==(8191&e)&&(r+=13,e>>>=13),0==(127&e)&&(r+=7,e>>>=7),0==(15&e)&&(r+=4,e>>>=4),0==(3&e)&&(r+=2,e>>>=2),0==(1&e)&&r++,r},n.prototype.bitLength=function(){var t=this.words[this.length-1],e=this._countBits(t);return 26*(this.length-1)+e},n.prototype.zeroBits=function(){if(this.isZero())return 0;for(var t=0,e=0;et.length?this.clone().ior(t):t.clone().ior(this)},n.prototype.uor=function(t){return this.length>t.length?this.clone().iuor(t):t.clone().iuor(this)},n.prototype.iuand=function(t){var e;e=this.length>t.length?t:this;for(var r=0;rt.length?this.clone().iand(t):t.clone().iand(this)},n.prototype.uand=function(t){return this.length>t.length?this.clone().iuand(t):t.clone().iuand(this)},n.prototype.iuxor=function(t){var e,r;this.length>t.length?(e=this,r=t):(e=t,r=this);for(var i=0;it.length?this.clone().ixor(t):t.clone().ixor(this)},n.prototype.uxor=function(t){return this.length>t.length?this.clone().iuxor(t):t.clone().iuxor(this)},n.prototype.inotn=function(t){r("number"==typeof t&&t>=0);var e=0|Math.ceil(t/26),i=t%26;this._expand(e),i>0&&e--;for(var n=0;n0&&(this.words[n]=~this.words[n]&67108863>>26-i),this.strip()},n.prototype.notn=function(t){return this.clone().inotn(t)},n.prototype.setn=function(t,e){r("number"==typeof t&&t>=0);var i=t/26|0,n=t%26;return this._expand(i+1),this.words[i]=e?this.words[i]|1<t.length?(r=this,i=t):(r=t,i=this);for(var n=0,o=0;o>>26;for(;0!==n&&o>>26;if(this.length=r.length,0!==n)this.words[this.length]=n,this.length++;else if(r!==this)for(;ot.length?this.clone().iadd(t):t.clone().iadd(this)},n.prototype.isub=function(t){if(0!==t.negative){t.negative=0;var e=this.iadd(t);return t.negative=1,e._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(t),this.negative=1,this._normSign();var r,i,n=this.cmp(t);if(0===n)return this.negative=0,this.length=1,this.words[0]=0,this;n>0?(r=this,i=t):(r=t,i=this);for(var o=0,s=0;s>26,this.words[s]=67108863&e;for(;0!==o&&s>26,this.words[s]=67108863&e;if(0===o&&s>>13,c=0|s[1],p=8191&c,m=c>>>13,g=0|s[2],v=8191&g,b=g>>>13,y=0|s[3],w=8191&y,M=y>>>13,_=0|s[4],A=8191&_,E=_>>>13,S=0|s[5],k=8191&S,B=S>>>13,R=0|s[6],T=8191&R,x=R>>>13,I=0|s[7],C=8191&I,P=I>>>13,D=0|s[8],L=8191&D,O=D>>>13,j=0|s[9],U=8191&j,N=j>>>13,z=0|a[0],q=8191&z,F=z>>>13,Z=0|a[1],H=8191&Z,K=Z>>>13,W=0|a[2],G=8191&W,Y=W>>>13,V=0|a[3],$=8191&V,X=V>>>13,J=0|a[4],Q=8191&J,tt=J>>>13,et=0|a[5],rt=8191&et,it=et>>>13,nt=0|a[6],ot=8191&nt,st=nt>>>13,at=0|a[7],ht=8191&at,ut=at>>>13,ft=0|a[8],lt=8191&ft,dt=ft>>>13,ct=0|a[9],pt=8191&ct,mt=ct>>>13;r.negative=t.negative^e.negative,r.length=19;var gt=(u+(i=Math.imul(l,q))|0)+((8191&(n=(n=Math.imul(l,F))+Math.imul(d,q)|0))<<13)|0;u=((o=Math.imul(d,F))+(n>>>13)|0)+(gt>>>26)|0,gt&=67108863,i=Math.imul(p,q),n=(n=Math.imul(p,F))+Math.imul(m,q)|0,o=Math.imul(m,F);var vt=(u+(i=i+Math.imul(l,H)|0)|0)+((8191&(n=(n=n+Math.imul(l,K)|0)+Math.imul(d,H)|0))<<13)|0;u=((o=o+Math.imul(d,K)|0)+(n>>>13)|0)+(vt>>>26)|0,vt&=67108863,i=Math.imul(v,q),n=(n=Math.imul(v,F))+Math.imul(b,q)|0,o=Math.imul(b,F),i=i+Math.imul(p,H)|0,n=(n=n+Math.imul(p,K)|0)+Math.imul(m,H)|0,o=o+Math.imul(m,K)|0;var bt=(u+(i=i+Math.imul(l,G)|0)|0)+((8191&(n=(n=n+Math.imul(l,Y)|0)+Math.imul(d,G)|0))<<13)|0;u=((o=o+Math.imul(d,Y)|0)+(n>>>13)|0)+(bt>>>26)|0,bt&=67108863,i=Math.imul(w,q),n=(n=Math.imul(w,F))+Math.imul(M,q)|0,o=Math.imul(M,F),i=i+Math.imul(v,H)|0,n=(n=n+Math.imul(v,K)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,K)|0,i=i+Math.imul(p,G)|0,n=(n=n+Math.imul(p,Y)|0)+Math.imul(m,G)|0,o=o+Math.imul(m,Y)|0;var yt=(u+(i=i+Math.imul(l,$)|0)|0)+((8191&(n=(n=n+Math.imul(l,X)|0)+Math.imul(d,$)|0))<<13)|0;u=((o=o+Math.imul(d,X)|0)+(n>>>13)|0)+(yt>>>26)|0,yt&=67108863,i=Math.imul(A,q),n=(n=Math.imul(A,F))+Math.imul(E,q)|0,o=Math.imul(E,F),i=i+Math.imul(w,H)|0,n=(n=n+Math.imul(w,K)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,K)|0,i=i+Math.imul(v,G)|0,n=(n=n+Math.imul(v,Y)|0)+Math.imul(b,G)|0,o=o+Math.imul(b,Y)|0,i=i+Math.imul(p,$)|0,n=(n=n+Math.imul(p,X)|0)+Math.imul(m,$)|0,o=o+Math.imul(m,X)|0;var wt=(u+(i=i+Math.imul(l,Q)|0)|0)+((8191&(n=(n=n+Math.imul(l,tt)|0)+Math.imul(d,Q)|0))<<13)|0;u=((o=o+Math.imul(d,tt)|0)+(n>>>13)|0)+(wt>>>26)|0,wt&=67108863,i=Math.imul(k,q),n=(n=Math.imul(k,F))+Math.imul(B,q)|0,o=Math.imul(B,F),i=i+Math.imul(A,H)|0,n=(n=n+Math.imul(A,K)|0)+Math.imul(E,H)|0,o=o+Math.imul(E,K)|0,i=i+Math.imul(w,G)|0,n=(n=n+Math.imul(w,Y)|0)+Math.imul(M,G)|0,o=o+Math.imul(M,Y)|0,i=i+Math.imul(v,$)|0,n=(n=n+Math.imul(v,X)|0)+Math.imul(b,$)|0,o=o+Math.imul(b,X)|0,i=i+Math.imul(p,Q)|0,n=(n=n+Math.imul(p,tt)|0)+Math.imul(m,Q)|0,o=o+Math.imul(m,tt)|0;var Mt=(u+(i=i+Math.imul(l,rt)|0)|0)+((8191&(n=(n=n+Math.imul(l,it)|0)+Math.imul(d,rt)|0))<<13)|0;u=((o=o+Math.imul(d,it)|0)+(n>>>13)|0)+(Mt>>>26)|0,Mt&=67108863,i=Math.imul(T,q),n=(n=Math.imul(T,F))+Math.imul(x,q)|0,o=Math.imul(x,F),i=i+Math.imul(k,H)|0,n=(n=n+Math.imul(k,K)|0)+Math.imul(B,H)|0,o=o+Math.imul(B,K)|0,i=i+Math.imul(A,G)|0,n=(n=n+Math.imul(A,Y)|0)+Math.imul(E,G)|0,o=o+Math.imul(E,Y)|0,i=i+Math.imul(w,$)|0,n=(n=n+Math.imul(w,X)|0)+Math.imul(M,$)|0,o=o+Math.imul(M,X)|0,i=i+Math.imul(v,Q)|0,n=(n=n+Math.imul(v,tt)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,tt)|0,i=i+Math.imul(p,rt)|0,n=(n=n+Math.imul(p,it)|0)+Math.imul(m,rt)|0,o=o+Math.imul(m,it)|0;var _t=(u+(i=i+Math.imul(l,ot)|0)|0)+((8191&(n=(n=n+Math.imul(l,st)|0)+Math.imul(d,ot)|0))<<13)|0;u=((o=o+Math.imul(d,st)|0)+(n>>>13)|0)+(_t>>>26)|0,_t&=67108863,i=Math.imul(C,q),n=(n=Math.imul(C,F))+Math.imul(P,q)|0,o=Math.imul(P,F),i=i+Math.imul(T,H)|0,n=(n=n+Math.imul(T,K)|0)+Math.imul(x,H)|0,o=o+Math.imul(x,K)|0,i=i+Math.imul(k,G)|0,n=(n=n+Math.imul(k,Y)|0)+Math.imul(B,G)|0,o=o+Math.imul(B,Y)|0,i=i+Math.imul(A,$)|0,n=(n=n+Math.imul(A,X)|0)+Math.imul(E,$)|0,o=o+Math.imul(E,X)|0,i=i+Math.imul(w,Q)|0,n=(n=n+Math.imul(w,tt)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,tt)|0,i=i+Math.imul(v,rt)|0,n=(n=n+Math.imul(v,it)|0)+Math.imul(b,rt)|0,o=o+Math.imul(b,it)|0,i=i+Math.imul(p,ot)|0,n=(n=n+Math.imul(p,st)|0)+Math.imul(m,ot)|0,o=o+Math.imul(m,st)|0;var At=(u+(i=i+Math.imul(l,ht)|0)|0)+((8191&(n=(n=n+Math.imul(l,ut)|0)+Math.imul(d,ht)|0))<<13)|0;u=((o=o+Math.imul(d,ut)|0)+(n>>>13)|0)+(At>>>26)|0,At&=67108863,i=Math.imul(L,q),n=(n=Math.imul(L,F))+Math.imul(O,q)|0,o=Math.imul(O,F),i=i+Math.imul(C,H)|0,n=(n=n+Math.imul(C,K)|0)+Math.imul(P,H)|0,o=o+Math.imul(P,K)|0,i=i+Math.imul(T,G)|0,n=(n=n+Math.imul(T,Y)|0)+Math.imul(x,G)|0,o=o+Math.imul(x,Y)|0,i=i+Math.imul(k,$)|0,n=(n=n+Math.imul(k,X)|0)+Math.imul(B,$)|0,o=o+Math.imul(B,X)|0,i=i+Math.imul(A,Q)|0,n=(n=n+Math.imul(A,tt)|0)+Math.imul(E,Q)|0,o=o+Math.imul(E,tt)|0,i=i+Math.imul(w,rt)|0,n=(n=n+Math.imul(w,it)|0)+Math.imul(M,rt)|0,o=o+Math.imul(M,it)|0,i=i+Math.imul(v,ot)|0,n=(n=n+Math.imul(v,st)|0)+Math.imul(b,ot)|0,o=o+Math.imul(b,st)|0,i=i+Math.imul(p,ht)|0,n=(n=n+Math.imul(p,ut)|0)+Math.imul(m,ht)|0,o=o+Math.imul(m,ut)|0;var Et=(u+(i=i+Math.imul(l,lt)|0)|0)+((8191&(n=(n=n+Math.imul(l,dt)|0)+Math.imul(d,lt)|0))<<13)|0;u=((o=o+Math.imul(d,dt)|0)+(n>>>13)|0)+(Et>>>26)|0,Et&=67108863,i=Math.imul(U,q),n=(n=Math.imul(U,F))+Math.imul(N,q)|0,o=Math.imul(N,F),i=i+Math.imul(L,H)|0,n=(n=n+Math.imul(L,K)|0)+Math.imul(O,H)|0,o=o+Math.imul(O,K)|0,i=i+Math.imul(C,G)|0,n=(n=n+Math.imul(C,Y)|0)+Math.imul(P,G)|0,o=o+Math.imul(P,Y)|0,i=i+Math.imul(T,$)|0,n=(n=n+Math.imul(T,X)|0)+Math.imul(x,$)|0,o=o+Math.imul(x,X)|0,i=i+Math.imul(k,Q)|0,n=(n=n+Math.imul(k,tt)|0)+Math.imul(B,Q)|0,o=o+Math.imul(B,tt)|0,i=i+Math.imul(A,rt)|0,n=(n=n+Math.imul(A,it)|0)+Math.imul(E,rt)|0,o=o+Math.imul(E,it)|0,i=i+Math.imul(w,ot)|0,n=(n=n+Math.imul(w,st)|0)+Math.imul(M,ot)|0,o=o+Math.imul(M,st)|0,i=i+Math.imul(v,ht)|0,n=(n=n+Math.imul(v,ut)|0)+Math.imul(b,ht)|0,o=o+Math.imul(b,ut)|0,i=i+Math.imul(p,lt)|0,n=(n=n+Math.imul(p,dt)|0)+Math.imul(m,lt)|0,o=o+Math.imul(m,dt)|0;var St=(u+(i=i+Math.imul(l,pt)|0)|0)+((8191&(n=(n=n+Math.imul(l,mt)|0)+Math.imul(d,pt)|0))<<13)|0;u=((o=o+Math.imul(d,mt)|0)+(n>>>13)|0)+(St>>>26)|0,St&=67108863,i=Math.imul(U,H),n=(n=Math.imul(U,K))+Math.imul(N,H)|0,o=Math.imul(N,K),i=i+Math.imul(L,G)|0,n=(n=n+Math.imul(L,Y)|0)+Math.imul(O,G)|0,o=o+Math.imul(O,Y)|0,i=i+Math.imul(C,$)|0,n=(n=n+Math.imul(C,X)|0)+Math.imul(P,$)|0,o=o+Math.imul(P,X)|0,i=i+Math.imul(T,Q)|0,n=(n=n+Math.imul(T,tt)|0)+Math.imul(x,Q)|0,o=o+Math.imul(x,tt)|0,i=i+Math.imul(k,rt)|0,n=(n=n+Math.imul(k,it)|0)+Math.imul(B,rt)|0,o=o+Math.imul(B,it)|0,i=i+Math.imul(A,ot)|0,n=(n=n+Math.imul(A,st)|0)+Math.imul(E,ot)|0,o=o+Math.imul(E,st)|0,i=i+Math.imul(w,ht)|0,n=(n=n+Math.imul(w,ut)|0)+Math.imul(M,ht)|0,o=o+Math.imul(M,ut)|0,i=i+Math.imul(v,lt)|0,n=(n=n+Math.imul(v,dt)|0)+Math.imul(b,lt)|0,o=o+Math.imul(b,dt)|0;var kt=(u+(i=i+Math.imul(p,pt)|0)|0)+((8191&(n=(n=n+Math.imul(p,mt)|0)+Math.imul(m,pt)|0))<<13)|0;u=((o=o+Math.imul(m,mt)|0)+(n>>>13)|0)+(kt>>>26)|0,kt&=67108863,i=Math.imul(U,G),n=(n=Math.imul(U,Y))+Math.imul(N,G)|0,o=Math.imul(N,Y),i=i+Math.imul(L,$)|0,n=(n=n+Math.imul(L,X)|0)+Math.imul(O,$)|0,o=o+Math.imul(O,X)|0,i=i+Math.imul(C,Q)|0,n=(n=n+Math.imul(C,tt)|0)+Math.imul(P,Q)|0,o=o+Math.imul(P,tt)|0,i=i+Math.imul(T,rt)|0,n=(n=n+Math.imul(T,it)|0)+Math.imul(x,rt)|0,o=o+Math.imul(x,it)|0,i=i+Math.imul(k,ot)|0,n=(n=n+Math.imul(k,st)|0)+Math.imul(B,ot)|0,o=o+Math.imul(B,st)|0,i=i+Math.imul(A,ht)|0,n=(n=n+Math.imul(A,ut)|0)+Math.imul(E,ht)|0,o=o+Math.imul(E,ut)|0,i=i+Math.imul(w,lt)|0,n=(n=n+Math.imul(w,dt)|0)+Math.imul(M,lt)|0,o=o+Math.imul(M,dt)|0;var Bt=(u+(i=i+Math.imul(v,pt)|0)|0)+((8191&(n=(n=n+Math.imul(v,mt)|0)+Math.imul(b,pt)|0))<<13)|0;u=((o=o+Math.imul(b,mt)|0)+(n>>>13)|0)+(Bt>>>26)|0,Bt&=67108863,i=Math.imul(U,$),n=(n=Math.imul(U,X))+Math.imul(N,$)|0,o=Math.imul(N,X),i=i+Math.imul(L,Q)|0,n=(n=n+Math.imul(L,tt)|0)+Math.imul(O,Q)|0,o=o+Math.imul(O,tt)|0,i=i+Math.imul(C,rt)|0,n=(n=n+Math.imul(C,it)|0)+Math.imul(P,rt)|0,o=o+Math.imul(P,it)|0,i=i+Math.imul(T,ot)|0,n=(n=n+Math.imul(T,st)|0)+Math.imul(x,ot)|0,o=o+Math.imul(x,st)|0,i=i+Math.imul(k,ht)|0,n=(n=n+Math.imul(k,ut)|0)+Math.imul(B,ht)|0,o=o+Math.imul(B,ut)|0,i=i+Math.imul(A,lt)|0,n=(n=n+Math.imul(A,dt)|0)+Math.imul(E,lt)|0,o=o+Math.imul(E,dt)|0;var Rt=(u+(i=i+Math.imul(w,pt)|0)|0)+((8191&(n=(n=n+Math.imul(w,mt)|0)+Math.imul(M,pt)|0))<<13)|0;u=((o=o+Math.imul(M,mt)|0)+(n>>>13)|0)+(Rt>>>26)|0,Rt&=67108863,i=Math.imul(U,Q),n=(n=Math.imul(U,tt))+Math.imul(N,Q)|0,o=Math.imul(N,tt),i=i+Math.imul(L,rt)|0,n=(n=n+Math.imul(L,it)|0)+Math.imul(O,rt)|0,o=o+Math.imul(O,it)|0,i=i+Math.imul(C,ot)|0,n=(n=n+Math.imul(C,st)|0)+Math.imul(P,ot)|0,o=o+Math.imul(P,st)|0,i=i+Math.imul(T,ht)|0,n=(n=n+Math.imul(T,ut)|0)+Math.imul(x,ht)|0,o=o+Math.imul(x,ut)|0,i=i+Math.imul(k,lt)|0,n=(n=n+Math.imul(k,dt)|0)+Math.imul(B,lt)|0,o=o+Math.imul(B,dt)|0;var Tt=(u+(i=i+Math.imul(A,pt)|0)|0)+((8191&(n=(n=n+Math.imul(A,mt)|0)+Math.imul(E,pt)|0))<<13)|0;u=((o=o+Math.imul(E,mt)|0)+(n>>>13)|0)+(Tt>>>26)|0,Tt&=67108863,i=Math.imul(U,rt),n=(n=Math.imul(U,it))+Math.imul(N,rt)|0,o=Math.imul(N,it),i=i+Math.imul(L,ot)|0,n=(n=n+Math.imul(L,st)|0)+Math.imul(O,ot)|0,o=o+Math.imul(O,st)|0,i=i+Math.imul(C,ht)|0,n=(n=n+Math.imul(C,ut)|0)+Math.imul(P,ht)|0,o=o+Math.imul(P,ut)|0,i=i+Math.imul(T,lt)|0,n=(n=n+Math.imul(T,dt)|0)+Math.imul(x,lt)|0,o=o+Math.imul(x,dt)|0;var xt=(u+(i=i+Math.imul(k,pt)|0)|0)+((8191&(n=(n=n+Math.imul(k,mt)|0)+Math.imul(B,pt)|0))<<13)|0;u=((o=o+Math.imul(B,mt)|0)+(n>>>13)|0)+(xt>>>26)|0,xt&=67108863,i=Math.imul(U,ot),n=(n=Math.imul(U,st))+Math.imul(N,ot)|0,o=Math.imul(N,st),i=i+Math.imul(L,ht)|0,n=(n=n+Math.imul(L,ut)|0)+Math.imul(O,ht)|0,o=o+Math.imul(O,ut)|0,i=i+Math.imul(C,lt)|0,n=(n=n+Math.imul(C,dt)|0)+Math.imul(P,lt)|0,o=o+Math.imul(P,dt)|0;var It=(u+(i=i+Math.imul(T,pt)|0)|0)+((8191&(n=(n=n+Math.imul(T,mt)|0)+Math.imul(x,pt)|0))<<13)|0;u=((o=o+Math.imul(x,mt)|0)+(n>>>13)|0)+(It>>>26)|0,It&=67108863,i=Math.imul(U,ht),n=(n=Math.imul(U,ut))+Math.imul(N,ht)|0,o=Math.imul(N,ut),i=i+Math.imul(L,lt)|0,n=(n=n+Math.imul(L,dt)|0)+Math.imul(O,lt)|0,o=o+Math.imul(O,dt)|0;var Ct=(u+(i=i+Math.imul(C,pt)|0)|0)+((8191&(n=(n=n+Math.imul(C,mt)|0)+Math.imul(P,pt)|0))<<13)|0;u=((o=o+Math.imul(P,mt)|0)+(n>>>13)|0)+(Ct>>>26)|0,Ct&=67108863,i=Math.imul(U,lt),n=(n=Math.imul(U,dt))+Math.imul(N,lt)|0,o=Math.imul(N,dt);var Pt=(u+(i=i+Math.imul(L,pt)|0)|0)+((8191&(n=(n=n+Math.imul(L,mt)|0)+Math.imul(O,pt)|0))<<13)|0;u=((o=o+Math.imul(O,mt)|0)+(n>>>13)|0)+(Pt>>>26)|0,Pt&=67108863;var Dt=(u+(i=Math.imul(U,pt))|0)+((8191&(n=(n=Math.imul(U,mt))+Math.imul(N,pt)|0))<<13)|0;return u=((o=Math.imul(N,mt))+(n>>>13)|0)+(Dt>>>26)|0,Dt&=67108863,h[0]=gt,h[1]=vt,h[2]=bt,h[3]=yt,h[4]=wt,h[5]=Mt,h[6]=_t,h[7]=At,h[8]=Et,h[9]=St,h[10]=kt,h[11]=Bt,h[12]=Rt,h[13]=Tt,h[14]=xt,h[15]=It,h[16]=Ct,h[17]=Pt,h[18]=Dt,0!==u&&(h[19]=u,r.length++),r};function p(t,e,r){return(new m).mulp(t,e,r)}function m(t,e){this.x=t,this.y=e}Math.imul||(c=d),n.prototype.mulTo=function(t,e){var r,i=this.length+t.length;return r=10===this.length&&10===t.length?c(this,t,e):i<63?d(this,t,e):i<1024?function(t,e,r){r.negative=e.negative^t.negative,r.length=t.length+e.length;for(var i=0,n=0,o=0;o>>26)|0)>>>26,s&=67108863}r.words[o]=a,i=s,s=n}return 0!==i?r.words[o]=i:r.length--,r.strip()}(this,t,e):p(this,t,e),r},m.prototype.makeRBT=function(t){for(var e=new Array(t),r=n.prototype._countBits(t)-1,i=0;i>=1;return i},m.prototype.permute=function(t,e,r,i,n,o){for(var s=0;s>>=1)n++;return 1<>>=13,i[2*s+1]=8191&o,o>>>=13;for(s=2*e;s>=26,e+=n/67108864|0,e+=o>>>26,this.words[i]=67108863&o}return 0!==e&&(this.words[i]=e,this.length++),this},n.prototype.muln=function(t){return this.clone().imuln(t)},n.prototype.sqr=function(){return this.mul(this)},n.prototype.isqr=function(){return this.imul(this.clone())},n.prototype.pow=function(t){var e=function(t){for(var e=new Array(t.bitLength()),r=0;r>>n}return e}(t);if(0===e.length)return new n(1);for(var r=this,i=0;i=0);var e,i=t%26,n=(t-i)/26,o=67108863>>>26-i<<26-i;if(0!==i){var s=0;for(e=0;e>>26-i}s&&(this.words[e]=s,this.length++)}if(0!==n){for(e=this.length-1;e>=0;e--)this.words[e+n]=this.words[e];for(e=0;e=0),n=e?(e-e%26)/26:0;var o=t%26,s=Math.min((t-o)/26,this.length),a=67108863^67108863>>>o<s)for(this.length-=s,u=0;u=0&&(0!==f||u>=n);u--){var l=0|this.words[u];this.words[u]=f<<26-o|l>>>o,f=l&a}return h&&0!==f&&(h.words[h.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},n.prototype.ishrn=function(t,e,i){return r(0===this.negative),this.iushrn(t,e,i)},n.prototype.shln=function(t){return this.clone().ishln(t)},n.prototype.ushln=function(t){return this.clone().iushln(t)},n.prototype.shrn=function(t){return this.clone().ishrn(t)},n.prototype.ushrn=function(t){return this.clone().iushrn(t)},n.prototype.testn=function(t){r("number"==typeof t&&t>=0);var e=t%26,i=(t-e)/26,n=1<=0);var e=t%26,i=(t-e)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=i)return this;if(0!==e&&i++,this.length=Math.min(i,this.length),0!==e){var n=67108863^67108863>>>e<=67108864;e++)this.words[e]-=67108864,e===this.length-1?this.words[e+1]=1:this.words[e+1]++;return this.length=Math.max(this.length,e+1),this},n.prototype.isubn=function(t){if(r("number"==typeof t),r(t<67108864),t<0)return this.iaddn(-t);if(0!==this.negative)return this.negative=0,this.iaddn(t),this.negative=1,this;if(this.words[0]-=t,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var e=0;e>26)-(h/67108864|0),this.words[n+i]=67108863&o}for(;n>26,this.words[n+i]=67108863&o;if(0===a)return this.strip();for(r(-1===a),a=0,n=0;n>26,this.words[n]=67108863&o;return this.negative=1,this.strip()},n.prototype._wordDiv=function(t,e){var r=(this.length,t.length),i=this.clone(),o=t,s=0|o.words[o.length-1];0!==(r=26-this._countBits(s))&&(o=o.ushln(r),i.iushln(r),s=0|o.words[o.length-1]);var a,h=i.length-o.length;if("mod"!==e){(a=new n(null)).length=h+1,a.words=new Array(a.length);for(var u=0;u=0;l--){var d=67108864*(0|i.words[o.length+l])+(0|i.words[o.length+l-1]);for(d=Math.min(d/s|0,67108863),i._ishlnsubmul(o,d,l);0!==i.negative;)d--,i.negative=0,i._ishlnsubmul(o,1,l),i.isZero()||(i.negative^=1);a&&(a.words[l]=d)}return a&&a.strip(),i.strip(),"div"!==e&&0!==r&&i.iushrn(r),{div:a||null,mod:i}},n.prototype.divmod=function(t,e,i){return r(!t.isZero()),this.isZero()?{div:new n(0),mod:new n(0)}:0!==this.negative&&0===t.negative?(a=this.neg().divmod(t,e),"mod"!==e&&(o=a.div.neg()),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.iadd(t)),{div:o,mod:s}):0===this.negative&&0!==t.negative?(a=this.divmod(t.neg(),e),"mod"!==e&&(o=a.div.neg()),{div:o,mod:a.mod}):0!=(this.negative&t.negative)?(a=this.neg().divmod(t.neg(),e),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.isub(t)),{div:a.div,mod:s}):t.length>this.length||this.cmp(t)<0?{div:new n(0),mod:this}:1===t.length?"div"===e?{div:this.divn(t.words[0]),mod:null}:"mod"===e?{div:null,mod:new n(this.modn(t.words[0]))}:{div:this.divn(t.words[0]),mod:new n(this.modn(t.words[0]))}:this._wordDiv(t,e);var o,s,a},n.prototype.div=function(t){return this.divmod(t,"div",!1).div},n.prototype.mod=function(t){return this.divmod(t,"mod",!1).mod},n.prototype.umod=function(t){return this.divmod(t,"mod",!0).mod},n.prototype.divRound=function(t){var e=this.divmod(t);if(e.mod.isZero())return e.div;var r=0!==e.div.negative?e.mod.isub(t):e.mod,i=t.ushrn(1),n=t.andln(1),o=r.cmp(i);return o<0||1===n&&0===o?e.div:0!==e.div.negative?e.div.isubn(1):e.div.iaddn(1)},n.prototype.modn=function(t){r(t<=67108863);for(var e=(1<<26)%t,i=0,n=this.length-1;n>=0;n--)i=(e*i+(0|this.words[n]))%t;return i},n.prototype.idivn=function(t){r(t<=67108863);for(var e=0,i=this.length-1;i>=0;i--){var n=(0|this.words[i])+67108864*e;this.words[i]=n/t|0,e=n%t}return this.strip()},n.prototype.divn=function(t){return this.clone().idivn(t)},n.prototype.egcd=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o=new n(1),s=new n(0),a=new n(0),h=new n(1),u=0;e.isEven()&&i.isEven();)e.iushrn(1),i.iushrn(1),++u;for(var f=i.clone(),l=e.clone();!e.isZero();){for(var d=0,c=1;0==(e.words[0]&c)&&d<26;++d,c<<=1);if(d>0)for(e.iushrn(d);d-- >0;)(o.isOdd()||s.isOdd())&&(o.iadd(f),s.isub(l)),o.iushrn(1),s.iushrn(1);for(var p=0,m=1;0==(i.words[0]&m)&&p<26;++p,m<<=1);if(p>0)for(i.iushrn(p);p-- >0;)(a.isOdd()||h.isOdd())&&(a.iadd(f),h.isub(l)),a.iushrn(1),h.iushrn(1);e.cmp(i)>=0?(e.isub(i),o.isub(a),s.isub(h)):(i.isub(e),a.isub(o),h.isub(s))}return{a:a,b:h,gcd:i.iushln(u)}},n.prototype._invmp=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o,s=new n(1),a=new n(0),h=i.clone();e.cmpn(1)>0&&i.cmpn(1)>0;){for(var u=0,f=1;0==(e.words[0]&f)&&u<26;++u,f<<=1);if(u>0)for(e.iushrn(u);u-- >0;)s.isOdd()&&s.iadd(h),s.iushrn(1);for(var l=0,d=1;0==(i.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(i.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(h),a.iushrn(1);e.cmp(i)>=0?(e.isub(i),s.isub(a)):(i.isub(e),a.isub(s))}return(o=0===e.cmpn(1)?s:a).cmpn(0)<0&&o.iadd(t),o},n.prototype.gcd=function(t){if(this.isZero())return t.abs();if(t.isZero())return this.abs();var e=this.clone(),r=t.clone();e.negative=0,r.negative=0;for(var i=0;e.isEven()&&r.isEven();i++)e.iushrn(1),r.iushrn(1);for(;;){for(;e.isEven();)e.iushrn(1);for(;r.isEven();)r.iushrn(1);var n=e.cmp(r);if(n<0){var o=e;e=r,r=o}else if(0===n||0===r.cmpn(1))break;e.isub(r)}return r.iushln(i)},n.prototype.invm=function(t){return this.egcd(t).a.umod(t)},n.prototype.isEven=function(){return 0==(1&this.words[0])},n.prototype.isOdd=function(){return 1==(1&this.words[0])},n.prototype.andln=function(t){return this.words[0]&t},n.prototype.bincn=function(t){r("number"==typeof t);var e=t%26,i=(t-e)/26,n=1<>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},n.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},n.prototype.cmpn=function(t){var e,i=t<0;if(0!==this.negative&&!i)return-1;if(0===this.negative&&i)return 1;if(this.strip(),this.length>1)e=1;else{i&&(t=-t),r(t<=67108863,"Number is too big");var n=0|this.words[0];e=n===t?0:nt.length)return 1;if(this.length=0;r--){var i=0|this.words[r],n=0|t.words[r];if(i!==n){in&&(e=1);break}}return e},n.prototype.gtn=function(t){return 1===this.cmpn(t)},n.prototype.gt=function(t){return 1===this.cmp(t)},n.prototype.gten=function(t){return this.cmpn(t)>=0},n.prototype.gte=function(t){return this.cmp(t)>=0},n.prototype.ltn=function(t){return-1===this.cmpn(t)},n.prototype.lt=function(t){return-1===this.cmp(t)},n.prototype.lten=function(t){return this.cmpn(t)<=0},n.prototype.lte=function(t){return this.cmp(t)<=0},n.prototype.eqn=function(t){return 0===this.cmpn(t)},n.prototype.eq=function(t){return 0===this.cmp(t)},n.red=function(t){return new _(t)},n.prototype.toRed=function(t){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),t.convertTo(this)._forceRed(t)},n.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},n.prototype._forceRed=function(t){return this.red=t,this},n.prototype.forceRed=function(t){return r(!this.red,"Already a number in reduction context"),this._forceRed(t)},n.prototype.redAdd=function(t){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,t)},n.prototype.redIAdd=function(t){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,t)},n.prototype.redSub=function(t){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,t)},n.prototype.redISub=function(t){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,t)},n.prototype.redShl=function(t){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,t)},n.prototype.redMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.mul(this,t)},n.prototype.redIMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.imul(this,t)},n.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},n.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},n.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},n.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},n.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},n.prototype.redPow=function(t){return r(this.red&&!t.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,t)};var g={k256:null,p224:null,p192:null,p25519:null};function v(t,e){this.name=t,this.p=new n(e,16),this.n=this.p.bitLength(),this.k=new n(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function b(){v.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function y(){v.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function w(){v.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function M(){v.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function _(t){if("string"==typeof t){var e=n._prime(t);this.m=e.p,this.prime=e}else r(t.gtn(1),"modulus must be greater than 1"),this.m=t,this.prime=null}function A(t){_.call(this,t),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new n(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}v.prototype._tmp=function(){var t=new n(null);return t.words=new Array(Math.ceil(this.n/13)),t},v.prototype.ireduce=function(t){var e,r=t;do{this.split(r,this.tmp),e=(r=(r=this.imulK(r)).iadd(this.tmp)).bitLength()}while(e>this.n);var i=e0?r.isub(this.p):void 0!==r.strip?r.strip():r._strip(),r},v.prototype.split=function(t,e){t.iushrn(this.n,0,e)},v.prototype.imulK=function(t){return t.imul(this.k)},i(b,v),b.prototype.split=function(t,e){for(var r=4194303,i=Math.min(t.length,9),n=0;n>>22,o=s}o>>>=22,t.words[n-10]=o,0===o&&t.length>10?t.length-=10:t.length-=9},b.prototype.imulK=function(t){t.words[t.length]=0,t.words[t.length+1]=0,t.length+=2;for(var e=0,r=0;r>>=26,t.words[r]=n,e=i}return 0!==e&&(t.words[t.length++]=e),t},n._prime=function(t){if(g[t])return g[t];var e;if("k256"===t)e=new b;else if("p224"===t)e=new y;else if("p192"===t)e=new w;else{if("p25519"!==t)throw new Error("Unknown prime "+t);e=new M}return g[t]=e,e},_.prototype._verify1=function(t){r(0===t.negative,"red works only with positives"),r(t.red,"red works only with red numbers")},_.prototype._verify2=function(t,e){r(0==(t.negative|e.negative),"red works only with positives"),r(t.red&&t.red===e.red,"red works only with red numbers")},_.prototype.imod=function(t){return this.prime?this.prime.ireduce(t)._forceRed(this):t.umod(this.m)._forceRed(this)},_.prototype.neg=function(t){return t.isZero()?t.clone():this.m.sub(t)._forceRed(this)},_.prototype.add=function(t,e){this._verify2(t,e);var r=t.add(e);return r.cmp(this.m)>=0&&r.isub(this.m),r._forceRed(this)},_.prototype.iadd=function(t,e){this._verify2(t,e);var r=t.iadd(e);return r.cmp(this.m)>=0&&r.isub(this.m),r},_.prototype.sub=function(t,e){this._verify2(t,e);var r=t.sub(e);return r.cmpn(0)<0&&r.iadd(this.m),r._forceRed(this)},_.prototype.isub=function(t,e){this._verify2(t,e);var r=t.isub(e);return r.cmpn(0)<0&&r.iadd(this.m),r},_.prototype.shl=function(t,e){return this._verify1(t),this.imod(t.ushln(e))},_.prototype.imul=function(t,e){return this._verify2(t,e),this.imod(t.imul(e))},_.prototype.mul=function(t,e){return this._verify2(t,e),this.imod(t.mul(e))},_.prototype.isqr=function(t){return this.imul(t,t.clone())},_.prototype.sqr=function(t){return this.mul(t,t)},_.prototype.sqrt=function(t){if(t.isZero())return t.clone();var e=this.m.andln(3);if(r(e%2==1),3===e){var i=this.m.add(new n(1)).iushrn(2);return this.pow(t,i)}for(var o=this.m.subn(1),s=0;!o.isZero()&&0===o.andln(1);)s++,o.iushrn(1);r(!o.isZero());var a=new n(1).toRed(this),h=a.redNeg(),u=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new n(2*f*f).toRed(this);0!==this.pow(f,u).cmp(h);)f.redIAdd(h);for(var l=this.pow(f,o),d=this.pow(t,o.addn(1).iushrn(1)),c=this.pow(t,o),p=s;0!==c.cmp(a);){for(var m=c,g=0;0!==m.cmp(a);g++)m=m.redSqr();r(g=0;i--){for(var u=e.words[i],f=h-1;f>=0;f--){var l=u>>f&1;o!==r[0]&&(o=this.sqr(o)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===i&&0===f)&&(o=this.mul(o,r[s]),a=0,s=0)):a=0}h=26}return o},_.prototype.convertTo=function(t){var e=t.umod(this.m);return e===t?e.clone():e},_.prototype.convertFrom=function(t){var e=t.clone();return e.red=null,e},n.mont=function(t){return new A(t)},i(A,_),A.prototype.convertTo=function(t){return this.imod(t.ushln(this.shift))},A.prototype.convertFrom=function(t){var e=this.imod(t.mul(this.rinv));return e.red=null,e},A.prototype.imul=function(t,e){if(t.isZero()||e.isZero())return t.words[0]=0,t.length=1,t;var r=t.imul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),n=r.isub(i).iushrn(this.shift),o=n;return n.cmp(this.m)>=0?o=n.isub(this.m):n.cmpn(0)<0&&(o=n.iadd(this.m)),o._forceRed(this)},A.prototype.mul=function(t,e){if(t.isZero()||e.isZero())return new n(0)._forceRed(this);var r=t.mul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),o=r.isub(i).iushrn(this.shift),s=o;return o.cmp(this.m)>=0?s=o.isub(this.m):o.cmpn(0)<0&&(s=o.iadd(this.m)),s._forceRed(this)},A.prototype.invm=function(t){return this.imod(t._invmp(this.m).mul(this.r2))._forceRed(this)}}(void 0===t||t,this),t.exports}.call({})}function Vg(){return Gg||(Gg=!0,Yg()),Wg}var $g,Xg,Jg=!1;function Qg(t,e){if(Array.isArray(t))return t.slice();if(!t)return[];var r=[];if("string"!=typeof t){for(var i=0;i>8,s=255&n;o?r.push(o,s):r.push(s)}return r}function tv(t){return 1===t.length?"0"+t:t}function ev(t){for(var e="",r=0;r(n>>1)-1?(n>>1)-h:h,o.isubn(a)):a=0,i[s]=a,o.iushrn(1)}return i}function uv(t,e){var r=[[],[]];t=t.clone(),e=e.clone();for(var i,n=0,o=0;t.cmpn(-n)>0||e.cmpn(-o)>0;){var s,a,h=t.andln(3)+n&3,u=e.andln(3)+o&3;3===h&&(h=-1),3===u&&(u=-1),s=0==(1&h)?0:3!==(i=t.andln(7)+n&7)&&5!==i||2!==u?h:-h,r[0].push(s),a=0==(1&u)?0:3!==(i=e.andln(7)+o&7)&&5!==i||2!==h?u:-u,r[1].push(a),2*n===s+1&&(n=1-n),2*o===a+1&&(o=1-o),t.iushrn(1),e.iushrn(1)}return r}function fv(t,e,r){var i="_"+e;t.prototype[e]=function(){return void 0!==this[i]?this[i]:this[i]=r.call(this)}}function lv(t){return"string"==typeof t?nv.toArray(t,"hex"):t}function dv(t){return new ov(t,"hex","le")}function cv(){return av||(av=!0,nv=iv={},ov=Vg(),sv=el(),rv(),nv.assert=sv,nv.toArray=rv().toArray,nv.zero2=rv().zero2,nv.toHex=rv().toHex,nv.encode=rv().encode,nv.getNAF=hv,nv.getJSF=uv,nv.cachedProperty=fv,nv.parseBytes=lv,nv.intFromLE=dv),iv}var pv,mv,gv,vv,bv,yv=!1;function wv(t,e){this.type=t,this.p=new mv(e.p,16),this.red=e.prime?mv.red(e.prime):mv.mont(this.p),this.zero=new mv(0).toRed(this.red),this.one=new mv(1).toRed(this.red),this.two=new mv(2).toRed(this.red),this.n=e.n&&new mv(e.n,16),this.g=e.g&&this.pointFromJSON(e.g,e.gRed),this._wnafT1=new Array(4),this._wnafT2=new Array(4),this._wnafT3=new Array(4),this._wnafT4=new Array(4),this._bitLength=this.n?this.n.bitLength():0;var r=this.n&&this.p.div(this.n);!r||r.cmpn(100)>0?this.redN=null:(this._maxwellTrick=!0,this.redN=this.n.toRed(this.red))}function Mv(t,e){this.curve=t,this.type=e,this.precomputed=null}function _v(){return yv||(yv=!0,pv={},mv=Vg(),cv(),gv=cv().getNAF,vv=cv().getJSF,bv=cv().assert,pv=wv,wv.prototype.point=function(){throw new Error("Not implemented")},wv.prototype.validate=function(){throw new Error("Not implemented")},wv.prototype._fixedNafMul=function(t,e){bv(t.precomputed);var r=t._getDoubles(),i=gv(e,1,this._bitLength),n=(1<=o;h--)s=(s<<1)+i[h];a.push(s)}for(var u=this.jpoint(null,null,null),f=this.jpoint(null,null,null),l=n;l>0;l--){for(o=0;o=0;a--){for(var h=0;a>=0&&0===o[a];a--)h++;if(a>=0&&h++,s=s.dblp(h),a<0)break;var u=o[a];bv(0!==u),s="affine"===t.type?u>0?s.mixedAdd(n[u-1>>1]):s.mixedAdd(n[-u-1>>1].neg()):u>0?s.add(n[u-1>>1]):s.add(n[-u-1>>1].neg())}return"affine"===t.type?s.toP():s},wv.prototype._wnafMulAdd=function(t,e,r,i,n){var o,s,a,h=this._wnafT1,u=this._wnafT2,f=this._wnafT3,l=0;for(o=0;o=1;o-=2){var c=o-1,p=o;if(1===h[c]&&1===h[p]){var m=[e[c],null,null,e[p]];0===e[c].y.cmp(e[p].y)?(m[1]=e[c].add(e[p]),m[2]=e[c].toJ().mixedAdd(e[p].neg())):0===e[c].y.cmp(e[p].y.redNeg())?(m[1]=e[c].toJ().mixedAdd(e[p]),m[2]=e[c].add(e[p].neg())):(m[1]=e[c].toJ().mixedAdd(e[p]),m[2]=e[c].toJ().mixedAdd(e[p].neg()));var g=[-3,-1,-5,-7,0,7,5,1,3],v=vv(r[c],r[p]);for(l=Math.max(v[0].length,l),f[c]=new Array(l),f[p]=new Array(l),s=0;s=0;o--){for(var _=0;o>=0;){var A=!0;for(s=0;s=0&&_++,w=w.dblp(_),o<0)break;for(s=0;s0?a=u[s][E-1>>1]:E<0&&(a=u[s][-E-1>>1].neg()),w="affine"===a.type?w.mixedAdd(a):w.add(a))}}for(o=0;o=Math.ceil((t.bitLength()+1)/e.step)},Mv.prototype._getDoubles=function(t,e){if(this.precomputed&&this.precomputed.doubles)return this.precomputed.doubles;for(var r=[this],i=this,n=0;n=0&&(o=e,s=r),i.negative&&(i=i.neg(),n=n.neg()),o.negative&&(o=o.neg(),s=s.neg()),[{a:i,b:n},{a:o,b:s}]},Tv.prototype._endoSplit=function(t){var e=this.endo.basis,r=e[0],i=e[1],n=i.b.mul(t).divRound(this.n),o=r.b.neg().mul(t).divRound(this.n),s=n.mul(r.a),a=o.mul(i.a),h=n.mul(r.b),u=o.mul(i.b);return{k1:t.sub(s).sub(a),k2:h.add(u).neg()}},Tv.prototype.pointFromX=function(t,e){(t=new Ev(t,16)).red||(t=t.toRed(this.red));var r=t.redSqr().redMul(t).redIAdd(t.redMul(this.a)).redIAdd(this.b),i=r.redSqrt();if(0!==i.redSqr().redSub(r).cmp(this.zero))throw new Error("invalid point");var n=i.fromRed().isOdd();return(e&&!n||!e&&n)&&(i=i.redNeg()),this.point(t,i)},Tv.prototype.validate=function(t){if(t.inf)return!0;var e=t.x,r=t.y,i=this.a.redMul(e),n=e.redSqr().redMul(e).redIAdd(i).redIAdd(this.b);return 0===r.redSqr().redISub(n).cmpn(0)},Tv.prototype._endoWnafMulAdd=function(t,e,r){for(var i=this._endoWnafT1,n=this._endoWnafT2,o=0;o":""},xv.prototype.isInfinity=function(){return this.inf},xv.prototype.add=function(t){if(this.inf)return t;if(t.inf)return this;if(this.eq(t))return this.dbl();if(this.neg().eq(t))return this.curve.point(null,null);if(0===this.x.cmp(t.x))return this.curve.point(null,null);var e=this.y.redSub(t.y);0!==e.cmpn(0)&&(e=e.redMul(this.x.redSub(t.x).redInvm()));var r=e.redSqr().redISub(this.x).redISub(t.x),i=e.redMul(this.x.redSub(r)).redISub(this.y);return this.curve.point(r,i)},xv.prototype.dbl=function(){if(this.inf)return this;var t=this.y.redAdd(this.y);if(0===t.cmpn(0))return this.curve.point(null,null);var e=this.curve.a,r=this.x.redSqr(),i=t.redInvm(),n=r.redAdd(r).redIAdd(r).redIAdd(e).redMul(i),o=n.redSqr().redISub(this.x.redAdd(this.x)),s=n.redMul(this.x.redSub(o)).redISub(this.y);return this.curve.point(o,s)},xv.prototype.getX=function(){return this.x.fromRed()},xv.prototype.getY=function(){return this.y.fromRed()},xv.prototype.mul=function(t){return t=new Ev(t,16),this.isInfinity()?this:this._hasDoubles(t)?this.curve._fixedNafMul(this,t):this.curve.endo?this.curve._endoWnafMulAdd([this],[t]):this.curve._wnafMul(this,t)},xv.prototype.mulAdd=function(t,e,r){var i=[this,e],n=[t,r];return this.curve.endo?this.curve._endoWnafMulAdd(i,n):this.curve._wnafMulAdd(1,i,n,2)},xv.prototype.jmulAdd=function(t,e,r){var i=[this,e],n=[t,r];return this.curve.endo?this.curve._endoWnafMulAdd(i,n,!0):this.curve._wnafMulAdd(1,i,n,2,!0)},xv.prototype.eq=function(t){return this===t||this.inf===t.inf&&(this.inf||0===this.x.cmp(t.x)&&0===this.y.cmp(t.y))},xv.prototype.neg=function(t){if(this.inf)return this;var e=this.curve.point(this.x,this.y.redNeg());if(t&&this.precomputed){var r=this.precomputed,i=function(t){return t.neg()};e.precomputed={naf:r.naf&&{wnd:r.naf.wnd,points:r.naf.points.map(i)},doubles:r.doubles&&{step:r.doubles.step,points:r.doubles.points.map(i)}}}return e},xv.prototype.toJ=function(){return this.inf?this.curve.jpoint(null,null,null):this.curve.jpoint(this.x,this.y,this.curve.one)},Sv(Iv,kv.BasePoint),Tv.prototype.jpoint=function(t,e,r){return new Iv(this,t,e,r)},Iv.prototype.toP=function(){if(this.isInfinity())return this.curve.point(null,null);var t=this.z.redInvm(),e=t.redSqr(),r=this.x.redMul(e),i=this.y.redMul(e).redMul(t);return this.curve.point(r,i)},Iv.prototype.neg=function(){return this.curve.jpoint(this.x,this.y.redNeg(),this.z)},Iv.prototype.add=function(t){if(this.isInfinity())return t;if(t.isInfinity())return this;var e=t.z.redSqr(),r=this.z.redSqr(),i=this.x.redMul(e),n=t.x.redMul(r),o=this.y.redMul(e.redMul(t.z)),s=t.y.redMul(r.redMul(this.z)),a=i.redSub(n),h=o.redSub(s);if(0===a.cmpn(0))return 0!==h.cmpn(0)?this.curve.jpoint(null,null,null):this.dbl();var u=a.redSqr(),f=u.redMul(a),l=i.redMul(u),d=h.redSqr().redIAdd(f).redISub(l).redISub(l),c=h.redMul(l.redISub(d)).redISub(o.redMul(f)),p=this.z.redMul(t.z).redMul(a);return this.curve.jpoint(d,c,p)},Iv.prototype.mixedAdd=function(t){if(this.isInfinity())return t.toJ();if(t.isInfinity())return this;var e=this.z.redSqr(),r=this.x,i=t.x.redMul(e),n=this.y,o=t.y.redMul(e).redMul(this.z),s=r.redSub(i),a=n.redSub(o);if(0===s.cmpn(0))return 0!==a.cmpn(0)?this.curve.jpoint(null,null,null):this.dbl();var h=s.redSqr(),u=h.redMul(s),f=r.redMul(h),l=a.redSqr().redIAdd(u).redISub(f).redISub(f),d=a.redMul(f.redISub(l)).redISub(n.redMul(u)),c=this.z.redMul(s);return this.curve.jpoint(l,d,c)},Iv.prototype.dblp=function(t){if(0===t)return this;if(this.isInfinity())return this;if(!t)return this.dbl();var e;if(this.curve.zeroA||this.curve.threeA){var r=this;for(e=0;e=0)return!1;if(r.redIAdd(n),0===this.x.cmp(r))return!0}},Iv.prototype.inspect=function(){return this.isInfinity()?"":""},Iv.prototype.isInfinity=function(){return 0===this.z.cmpn(0)}),Av}var Pv,Dv,Lv,Ov,jv=!1;function Uv(t){Ov.call(this,"mont",t),this.a=new Dv(t.a,16).toRed(this.red),this.b=new Dv(t.b,16).toRed(this.red),this.i4=new Dv(4).toRed(this.red).redInvm(),this.two=new Dv(2).toRed(this.red),this.a24=this.i4.redMul(this.a.redAdd(this.two))}function Nv(t,e,r){Ov.BasePoint.call(this,t,"projective"),null===e&&null===r?(this.x=this.curve.one,this.z=this.curve.zero):(this.x=new Dv(e,16),this.z=new Dv(r,16),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curve.red)))}function zv(){return jv||(jv=!0,Pv={},Dv=Vg(),Lv=Ar(),Ov=_v(),cv(),Lv(Uv,Ov),Pv=Uv,Uv.prototype.validate=function(t){var e=t.normalize().x,r=e.redSqr(),i=r.redMul(e).redAdd(r.redMul(this.a)).redAdd(e);return 0===i.redSqrt().redSqr().cmp(i)},Lv(Nv,Ov.BasePoint),Uv.prototype.decodePoint=function(t,e){return this.point(cv().toArray(t,e),1)},Uv.prototype.point=function(t,e){return new Nv(this,t,e)},Uv.prototype.pointFromJSON=function(t){return Nv.fromJSON(this,t)},Nv.prototype.precompute=function(){},Nv.prototype._encode=function(){return this.getX().toArray("be",this.curve.p.byteLength())},Nv.fromJSON=function(t,e){return new Nv(t,e[0],e[1]||t.one)},Nv.prototype.inspect=function(){return this.isInfinity()?"":""},Nv.prototype.isInfinity=function(){return 0===this.z.cmpn(0)},Nv.prototype.dbl=function(){var t=this.x.redAdd(this.z).redSqr(),e=this.x.redSub(this.z).redSqr(),r=t.redSub(e),i=t.redMul(e),n=r.redMul(e.redAdd(this.curve.a24.redMul(r)));return this.curve.point(i,n)},Nv.prototype.add=function(){throw new Error("Not supported on Montgomery curve")},Nv.prototype.diffAdd=function(t,e){var r=this.x.redAdd(this.z),i=this.x.redSub(this.z),n=t.x.redAdd(t.z),o=t.x.redSub(t.z).redMul(r),s=n.redMul(i),a=e.z.redMul(o.redAdd(s).redSqr()),h=e.x.redMul(o.redISub(s).redSqr());return this.curve.point(a,h)},Nv.prototype.mul=function(t){for(var e=t.clone(),r=this,i=this.curve.point(null,null),n=[];0!==e.cmpn(0);e.iushrn(1))n.push(e.andln(1));for(var o=n.length-1;o>=0;o--)0===n[o]?(r=r.diffAdd(i,this),i=i.dbl()):(i=r.diffAdd(i,this),r=r.dbl());return i},Nv.prototype.mulAdd=function(){throw new Error("Not supported on Montgomery curve")},Nv.prototype.jumlAdd=function(){throw new Error("Not supported on Montgomery curve")},Nv.prototype.eq=function(t){return 0===this.getX().cmp(t.getX())},Nv.prototype.normalize=function(){return this.x=this.x.redMul(this.z.redInvm()),this.z=this.curve.one,this},Nv.prototype.getX=function(){return this.normalize(),this.x.fromRed()}),Pv}var qv,Fv,Zv,Hv,Kv,Wv=!1;function Gv(t){this.twisted=1!=(0|t.a),this.mOneA=this.twisted&&-1==(0|t.a),this.extended=this.mOneA,Hv.call(this,"edwards",t),this.a=new Fv(t.a,16).umod(this.red.m),this.a=this.a.toRed(this.red),this.c=new Fv(t.c,16).toRed(this.red),this.c2=this.c.redSqr(),this.d=new Fv(t.d,16).toRed(this.red),this.dd=this.d.redAdd(this.d),Kv(!this.twisted||0===this.c.fromRed().cmpn(1)),this.oneC=1==(0|t.c)}function Yv(t,e,r,i,n){Hv.BasePoint.call(this,t,"projective"),null===e&&null===r&&null===i?(this.x=this.curve.zero,this.y=this.curve.one,this.z=this.curve.one,this.t=this.curve.zero,this.zOne=!0):(this.x=new Fv(e,16),this.y=new Fv(r,16),this.z=i?new Fv(i,16):this.curve.one,this.t=n&&new Fv(n,16),this.x.red||(this.x=this.x.toRed(this.curve.red)),this.y.red||(this.y=this.y.toRed(this.curve.red)),this.z.red||(this.z=this.z.toRed(this.curve.red)),this.t&&!this.t.red&&(this.t=this.t.toRed(this.curve.red)),this.zOne=this.z===this.curve.one,this.curve.extended&&!this.t&&(this.t=this.x.redMul(this.y),this.zOne||(this.t=this.t.redMul(this.z.redInvm()))))}function Vv(){return Wv||(Wv=!0,qv={},cv(),Fv=Vg(),Zv=Ar(),Hv=_v(),Kv=cv().assert,Zv(Gv,Hv),qv=Gv,Gv.prototype._mulA=function(t){return this.mOneA?t.redNeg():this.a.redMul(t)},Gv.prototype._mulC=function(t){return this.oneC?t:this.c.redMul(t)},Gv.prototype.jpoint=function(t,e,r,i){return this.point(t,e,r,i)},Gv.prototype.pointFromX=function(t,e){(t=new Fv(t,16)).red||(t=t.toRed(this.red));var r=t.redSqr(),i=this.c2.redSub(this.a.redMul(r)),n=this.one.redSub(this.c2.redMul(this.d).redMul(r)),o=i.redMul(n.redInvm()),s=o.redSqrt();if(0!==s.redSqr().redSub(o).cmp(this.zero))throw new Error("invalid point");var a=s.fromRed().isOdd();return(e&&!a||!e&&a)&&(s=s.redNeg()),this.point(t,s)},Gv.prototype.pointFromY=function(t,e){(t=new Fv(t,16)).red||(t=t.toRed(this.red));var r=t.redSqr(),i=r.redSub(this.c2),n=r.redMul(this.d).redMul(this.c2).redSub(this.a),o=i.redMul(n.redInvm());if(0===o.cmp(this.zero)){if(e)throw new Error("invalid point");return this.point(this.zero,t)}var s=o.redSqrt();if(0!==s.redSqr().redSub(o).cmp(this.zero))throw new Error("invalid point");return s.fromRed().isOdd()!==e&&(s=s.redNeg()),this.point(s,t)},Gv.prototype.validate=function(t){if(t.isInfinity())return!0;t.normalize();var e=t.x.redSqr(),r=t.y.redSqr(),i=e.redMul(this.a).redAdd(r),n=this.c2.redMul(this.one.redAdd(this.d.redMul(e).redMul(r)));return 0===i.cmp(n)},Zv(Yv,Hv.BasePoint),Gv.prototype.pointFromJSON=function(t){return Yv.fromJSON(this,t)},Gv.prototype.point=function(t,e,r,i){return new Yv(this,t,e,r,i)},Yv.fromJSON=function(t,e){return new Yv(t,e[0],e[1],e[2])},Yv.prototype.inspect=function(){return this.isInfinity()?"":""},Yv.prototype.isInfinity=function(){return 0===this.x.cmpn(0)&&(0===this.y.cmp(this.z)||this.zOne&&0===this.y.cmp(this.curve.c))},Yv.prototype._extDbl=function(){var t=this.x.redSqr(),e=this.y.redSqr(),r=this.z.redSqr();r=r.redIAdd(r);var i=this.curve._mulA(t),n=this.x.redAdd(this.y).redSqr().redISub(t).redISub(e),o=i.redAdd(e),s=o.redSub(r),a=i.redSub(e),h=n.redMul(s),u=o.redMul(a),f=n.redMul(a),l=s.redMul(o);return this.curve.point(h,u,l,f)},Yv.prototype._projDbl=function(){var t,e,r,i,n,o,s=this.x.redAdd(this.y).redSqr(),a=this.x.redSqr(),h=this.y.redSqr();if(this.curve.twisted){var u=(i=this.curve._mulA(a)).redAdd(h);this.zOne?(t=s.redSub(a).redSub(h).redMul(u.redSub(this.curve.two)),e=u.redMul(i.redSub(h)),r=u.redSqr().redSub(u).redSub(u)):(n=this.z.redSqr(),o=u.redSub(n).redISub(n),t=s.redSub(a).redISub(h).redMul(o),e=u.redMul(i.redSub(h)),r=u.redMul(o))}else i=a.redAdd(h),n=this.curve._mulC(this.z).redSqr(),o=i.redSub(n).redSub(n),t=this.curve._mulC(s.redISub(i)).redMul(o),e=this.curve._mulC(i).redMul(a.redISub(h)),r=i.redMul(o);return this.curve.point(t,e,r)},Yv.prototype.dbl=function(){return this.isInfinity()?this:this.curve.extended?this._extDbl():this._projDbl()},Yv.prototype._extAdd=function(t){var e=this.y.redSub(this.x).redMul(t.y.redSub(t.x)),r=this.y.redAdd(this.x).redMul(t.y.redAdd(t.x)),i=this.t.redMul(this.curve.dd).redMul(t.t),n=this.z.redMul(t.z.redAdd(t.z)),o=r.redSub(e),s=n.redSub(i),a=n.redAdd(i),h=r.redAdd(e),u=o.redMul(s),f=a.redMul(h),l=o.redMul(h),d=s.redMul(a);return this.curve.point(u,f,d,l)},Yv.prototype._projAdd=function(t){var e,r,i=this.z.redMul(t.z),n=i.redSqr(),o=this.x.redMul(t.x),s=this.y.redMul(t.y),a=this.curve.d.redMul(o).redMul(s),h=n.redSub(a),u=n.redAdd(a),f=this.x.redAdd(this.y).redMul(t.x.redAdd(t.y)).redISub(o).redISub(s),l=i.redMul(h).redMul(f);return this.curve.twisted?(e=i.redMul(u).redMul(s.redSub(this.curve._mulA(o))),r=h.redMul(u)):(e=i.redMul(u).redMul(s.redSub(o)),r=this.curve._mulC(h).redMul(u)),this.curve.point(l,e,r)},Yv.prototype.add=function(t){return this.isInfinity()?t:t.isInfinity()?this:this.curve.extended?this._extAdd(t):this._projAdd(t)},Yv.prototype.mul=function(t){return this._hasDoubles(t)?this.curve._fixedNafMul(this,t):this.curve._wnafMul(this,t)},Yv.prototype.mulAdd=function(t,e,r){return this.curve._wnafMulAdd(1,[this,e],[t,r],2,!1)},Yv.prototype.jmulAdd=function(t,e,r){return this.curve._wnafMulAdd(1,[this,e],[t,r],2,!0)},Yv.prototype.normalize=function(){if(this.zOne)return this;var t=this.z.redInvm();return this.x=this.x.redMul(t),this.y=this.y.redMul(t),this.t&&(this.t=this.t.redMul(t)),this.z=this.curve.one,this.zOne=!0,this},Yv.prototype.neg=function(){return this.curve.point(this.x.redNeg(),this.y,this.z,this.t&&this.t.redNeg())},Yv.prototype.getX=function(){return this.normalize(),this.x.fromRed()},Yv.prototype.getY=function(){return this.normalize(),this.y.fromRed()},Yv.prototype.eq=function(t){return this===t||0===this.getX().cmp(t.getX())&&0===this.getY().cmp(t.getY())},Yv.prototype.eqXToP=function(t){var e=t.toRed(this.curve.red).redMul(this.z);if(0===this.x.cmp(e))return!0;for(var r=t.clone(),i=this.curve.redN.redMul(this.z);;){if(r.iadd(this.curve.n),r.cmp(this.curve.p)>=0)return!1;if(e.redIAdd(i),0===this.x.cmp(e))return!0}},Yv.prototype.toP=Yv.prototype.normalize,Yv.prototype.mixedAdd=Yv.prototype.add),qv}var $v,Xv,Jv=!1;function Qv(){return Jv||(Jv=!0,(Xv=$v={}).base=_v(),Xv.short=Cv(),Xv.mont=zv(),Xv.edwards=Vv()),$v}var tb,eb,rb,ib,nb,ob,sb,ab,hb,ub,fb,lb,db,cb,pb,mb,gb,vb,bb,yb,wb,Mb,_b,Ab,Eb,Sb,kb,Bb,Rb,Tb=!1;function xb(t,e){return 55296==(64512&t.charCodeAt(e))&&(!(e<0||e+1>=t.length)&&56320==(64512&t.charCodeAt(e+1)))}function Ib(t,e){if(Array.isArray(t))return t.slice();if(!t)return[];var r=[];if("string"==typeof t)if(e){if("hex"===e)for((t=t.replace(/[^a-z0-9]+/gi,"")).length%2!=0&&(t="0"+t),n=0;n>6|192,r[i++]=63&o|128):xb(t,n)?(o=65536+((1023&o)<<10)+(1023&t.charCodeAt(++n)),r[i++]=o>>18|240,r[i++]=o>>12&63|128,r[i++]=o>>6&63|128,r[i++]=63&o|128):(r[i++]=o>>12|224,r[i++]=o>>6&63|128,r[i++]=63&o|128)}else for(n=0;n>>24|t>>>8&65280|t<<8&16711680|(255&t)<<24)>>>0}function Db(t,e){for(var r="",i=0;i>>0}return o}function Ub(t,e){for(var r=new Array(4*t.length),i=0,n=0;i>>24,r[n+1]=o>>>16&255,r[n+2]=o>>>8&255,r[n+3]=255&o):(r[n+3]=o>>>24,r[n+2]=o>>>16&255,r[n+1]=o>>>8&255,r[n]=255&o)}return r}function Nb(t,e){return t>>>e|t<<32-e}function zb(t,e){return t<>>32-e}function qb(t,e){return t+e>>>0}function Fb(t,e,r){return t+e+r>>>0}function Zb(t,e,r,i){return t+e+r+i>>>0}function Hb(t,e,r,i,n){return t+e+r+i+n>>>0}function Kb(t,e,r,i){var n=t[e],o=i+t[e+1]>>>0,s=(o>>0,t[e+1]=o}function Wb(t,e,r,i){return(e+i>>>0>>0}function Gb(t,e,r,i){return e+i>>>0}function Yb(t,e,r,i,n,o,s,a){var h=0,u=e;return h+=(u=u+i>>>0)>>0)>>0)>>0}function Vb(t,e,r,i,n,o,s,a){return e+i+o+a>>>0}function $b(t,e,r,i,n,o,s,a,h,u){var f=0,l=e;return f+=(l=l+i>>>0)>>0)>>0)>>0)>>0}function Xb(t,e,r,i,n,o,s,a,h,u){return e+i+o+a+u>>>0}function Jb(t,e,r){return(e<<32-r|t>>>r)>>>0}function Qb(t,e,r){return(t<<32-r|e>>>r)>>>0}function ty(t,e,r){return t>>>r}function ey(t,e,r){return(t<<32-r|e>>>r)>>>0}function ry(){return Tb||(Tb=!0,tb={},eb=el(),rb=Ar(),ib=rb,tb.inherits=ib,nb=Ib,tb.toArray=nb,ob=Cb,tb.toHex=ob,sb=Pb,tb.htonl=sb,ab=Db,tb.toHex32=ab,hb=Lb,tb.zero2=hb,ub=Ob,tb.zero8=ub,fb=jb,tb.join32=fb,lb=Ub,tb.split32=lb,db=Nb,tb.rotr32=db,cb=zb,tb.rotl32=cb,pb=qb,tb.sum32=pb,mb=Fb,tb.sum32_3=mb,gb=Zb,tb.sum32_4=gb,vb=Hb,tb.sum32_5=vb,bb=Kb,tb.sum64=bb,yb=Wb,tb.sum64_hi=yb,wb=Gb,tb.sum64_lo=wb,Mb=Yb,tb.sum64_4_hi=Mb,_b=Vb,tb.sum64_4_lo=_b,Ab=$b,tb.sum64_5_hi=Ab,Eb=Xb,tb.sum64_5_lo=Eb,Sb=Jb,tb.rotr64_hi=Sb,kb=Qb,tb.rotr64_lo=kb,Bb=ty,tb.shr64_hi=Bb,Rb=ey,tb.shr64_lo=Rb),tb}var iy,ny,oy,sy=!1;function ay(){this.pending=null,this.pendingTotal=0,this.blockSize=this.constructor.blockSize,this.outSize=this.constructor.outSize,this.hmacStrength=this.constructor.hmacStrength,this.padLength=this.constructor.padLength/8,this.endian="big",this._delta8=this.blockSize/8,this._delta32=this.blockSize/32}function hy(){return sy||(sy=!0,iy={},ry(),ny=el(),oy=ay,iy.BlockHash=oy,ay.prototype.update=function(t,e){if(t=ry().toArray(t,e),this.pending?this.pending=this.pending.concat(t):this.pending=t,this.pendingTotal+=t.length,this.pending.length>=this._delta8){var r=(t=this.pending).length%this._delta8;this.pending=t.slice(t.length-r,t.length),0===this.pending.length&&(this.pending=null),t=ry().join32(t,0,t.length-r,this.endian);for(var i=0;i>>24&255,i[n++]=t>>>16&255,i[n++]=t>>>8&255,i[n++]=255&t}else for(i[n++]=255&t,i[n++]=t>>>8&255,i[n++]=t>>>16&255,i[n++]=t>>>24&255,i[n++]=0,i[n++]=0,i[n++]=0,i[n++]=0,o=8;o>>3}function Sy(t){return fy(t,17)^fy(t,19)^t>>>10}function ky(){return by||(by=!0,uy={},ry(),fy=ry().rotr32,ly=yy,uy.ft_1=ly,dy=wy,uy.ch32=dy,cy=My,uy.maj32=cy,py=_y,uy.s0_256=py,my=Ay,uy.s1_256=my,gy=Ey,uy.g0_256=gy,vy=Sy,uy.g1_256=vy),uy}var By,Ry,Ty,xy,Iy,Cy,Py,Dy=!1;function Ly(){if(!(this instanceof Ly))return new Ly;Cy.call(this),this.h=[1732584193,4023233417,2562383102,271733878,3285377520],this.W=new Array(80)}function Oy(){return Dy||(Dy=!0,By={},ry(),hy(),ky(),Ry=ry().rotl32,Ty=ry().sum32,xy=ry().sum32_5,Iy=ky().ft_1,Cy=hy().BlockHash,Py=[1518500249,1859775393,2400959708,3395469782],ry().inherits(Ly,Cy),By=Ly,Ly.blockSize=512,Ly.outSize=160,Ly.hmacStrength=80,Ly.padLength=64,Ly.prototype._update=function(t,e){for(var r=this.W,i=0;i<16;i++)r[i]=t[e+i];for(;ithis.blockSize&&(t=(new this.Hash).update(t).digest()),uM(t.length<=this.blockSize);for(var e=t.length;e=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._init(e,r,i)}function CM(){return xM||(xM=!0,RM={},gM(),rv(),TM=el(),RM=IM,IM.prototype._init=function(t,e,r){var i=t.concat(e).concat(r);this.K=new Array(this.outLen/8),this.V=new Array(this.outLen/8);for(var n=0;n=this.minEntropy/8,"Not enough entropy. Minimum is: "+this.minEntropy+" bits"),this._update(t.concat(r||[])),this._reseed=1},IM.prototype.generate=function(t,e,r,i){if(this._reseed>this.reseedInterval)throw new Error("Reseed is required");"string"!=typeof e&&(i=r,r=e,e=null),r&&(r=rv().toArray(r,i||"hex"),this._update(r));for(var n=[];n.length"}),PM}var NM,zM,qM,FM=!1;function ZM(t,e){if(t instanceof ZM)return t;this._importDER(t,e)||(qM(t.r&&t.s,"Signature without r or s"),this.r=new zM(t.r,16),this.s=new zM(t.s,16),void 0===t.recoveryParam?this.recoveryParam=null:this.recoveryParam=t.recoveryParam)}function HM(){this.place=0}function KM(t,e){var r=t[e.place++];if(!(128&r))return r;var i=15&r;if(0===i||i>4)return!1;for(var n=0,o=0,s=e.place;o>>=0;return!(n<=127)&&(e.place=s,n)}function WM(t){for(var e=0,r=t.length-1;!t[e]&&!(128&t[e+1])&&e>>3);for(t.push(128|r);--r;)t.push(e>>>(r<<3)&255);t.push(e)}}function YM(){return FM||(FM=!0,NM={},zM=Vg(),cv(),qM=cv().assert,NM=ZM,ZM.prototype._importDER=function(t,e){t=cv().toArray(t,e);var r=new HM;if(48!==t[r.place++])return!1;var i=KM(t,r);if(!1===i)return!1;if(i+r.place!==t.length)return!1;if(2!==t[r.place++])return!1;var n=KM(t,r);if(!1===n)return!1;var o=t.slice(r.place,n+r.place);if(r.place+=n,2!==t[r.place++])return!1;var s=KM(t,r);if(!1===s)return!1;if(t.length!==s+r.place)return!1;var a=t.slice(r.place,s+r.place);if(0===o[0]){if(!(128&o[1]))return!1;o=o.slice(1)}if(0===a[0]){if(!(128&a[1]))return!1;a=a.slice(1)}return this.r=new zM(o),this.s=new zM(a),this.recoveryParam=null,!0},ZM.prototype.toDER=function(t){var e=this.r.toArray(),r=this.s.toArray();for(128&e[0]&&(e=[0].concat(e)),128&r[0]&&(r=[0].concat(r)),e=WM(e),r=WM(r);!(r[0]||128&r[1]);)r=r.slice(1);var i=[2];GM(i,e.length),(i=i.concat(e)).push(2),GM(i,r.length);var n=i.concat(r),o=[48];return GM(o,n.length),o=o.concat(n),cv().encode(o,t)}),NM}var VM,$M,XM,JM,QM,t_,e_,r_,i_=!1;function n_(t){if(!(this instanceof n_))return new n_(t);"string"==typeof t&&(t_(Object.prototype.hasOwnProperty.call(JM,t),"Unknown curve "+t),t=JM[t]),t instanceof JM.PresetCurve&&(t={curve:t}),this.curve=t.curve.curve,this.n=this.curve.n,this.nh=this.n.ushrn(1),this.g=this.curve.g,this.g=t.curve.g,this.g.precompute(t.curve.n.bitLength()+1),this.hash=t.hash||t.curve.hash}function o_(){return i_||(i_=!0,VM={},$M=Vg(),XM=CM(),cv(),JM=BM(),QM=Am(),t_=cv().assert,e_=UM(),r_=YM(),VM=n_,n_.prototype.keyPair=function(t){return new e_(this,t)},n_.prototype.keyFromPrivate=function(t,e){return e_.fromPrivate(this,t,e)},n_.prototype.keyFromPublic=function(t,e){return e_.fromPublic(this,t,e)},n_.prototype.genKeyPair=function(t){t||(t={});for(var e=new XM({hash:this.hash,pers:t.pers,persEnc:t.persEnc||"utf8",entropy:t.entropy||QM(this.hash.hmacStrength),entropyEnc:t.entropy&&t.entropyEnc||"utf8",nonce:this.n.toArray()}),r=this.n.byteLength(),i=this.n.sub(new $M(2));;){var n=new $M(e.generate(r));if(!(n.cmp(i)>0))return n.iaddn(1),this.keyFromPrivate(n)}},n_.prototype._truncateToN=function(t,e){var r=8*t.byteLength()-this.n.bitLength();return r>0&&(t=t.ushrn(r)),!e&&t.cmp(this.n)>=0?t.sub(this.n):t},n_.prototype.sign=function(t,e,r,i){"object"==typeof r&&(i=r,r=null),i||(i={}),e=this.keyFromPrivate(e,r),t=this._truncateToN(new $M(t,16));for(var n=this.n.byteLength(),o=e.getPrivate().toArray("be",n),s=t.toArray("be",n),a=new XM({hash:this.hash,entropy:o,nonce:s,pers:i.pers,persEnc:i.persEnc||"utf8"}),h=this.n.sub(new $M(1)),u=0;;u++){var f=i.k?i.k(u):new $M(a.generate(this.n.byteLength()));if(!((f=this._truncateToN(f,!0)).cmpn(1)<=0||f.cmp(h)>=0)){var l=this.g.mul(f);if(!l.isInfinity()){var d=l.getX(),c=d.umod(this.n);if(0!==c.cmpn(0)){var p=f.invm(this.n).mul(c.mul(e.getPrivate()).iadd(t));if(0!==(p=p.umod(this.n)).cmpn(0)){var m=(l.getY().isOdd()?1:0)|(0!==d.cmp(c)?2:0);return i.canonical&&p.cmp(this.nh)>0&&(p=this.n.sub(p),m^=1),new r_({r:c,s:p,recoveryParam:m})}}}}}},n_.prototype.verify=function(t,e,r,i){t=this._truncateToN(new $M(t,16)),r=this.keyFromPublic(r,i);var n=(e=new r_(e,"hex")).r,o=e.s;if(n.cmpn(1)<0||n.cmp(this.n)>=0)return!1;if(o.cmpn(1)<0||o.cmp(this.n)>=0)return!1;var s,a=o.invm(this.n),h=a.mul(t).umod(this.n),u=a.mul(n).umod(this.n);return this.curve._maxwellTrick?!(s=this.g.jmulAdd(h,r.getPublic(),u)).isInfinity()&&s.eqXToP(n):!(s=this.g.mulAdd(h,r.getPublic(),u)).isInfinity()&&0===s.getX().umod(this.n).cmp(n)},n_.prototype.recoverPubKey=function(t,e,r,i){t_((3&r)===r,"The recovery param is more than two bits"),e=new r_(e,i);var n=this.n,o=new $M(t),s=e.r,a=e.s,h=1&r,u=r>>1;if(s.cmp(this.curve.p.umod(this.curve.n))>=0&&u)throw new Error("Unable to find sencond key candinate");s=u?this.curve.pointFromX(s.add(this.curve.n),h):this.curve.pointFromX(s,h);var f=e.r.invm(n),l=n.sub(o).mul(f).umod(n),d=a.mul(f).umod(n);return this.g.mulAdd(l,s,d)},n_.prototype.getKeyRecoveryParam=function(t,e,r,i){if(null!==(e=new r_(e,i)).recoveryParam)return e.recoveryParam;for(var n=0;n<4;n++){var o;try{o=this.recoverPubKey(t,e,n)}catch(t){continue}if(o.eq(r))return n}throw new Error("Unable to find valid recovery factor")}),VM}var s_,a_,h_,u_,f_=!1;function l_(t,e){this.eddsa=t,this._secret=h_(e.secret),t.isPoint(e.pub)?this._pub=e.pub:this._pubBytes=h_(e.pub)}function d_(){return f_||(f_=!0,s_={},cv(),a_=cv().assert,h_=cv().parseBytes,u_=cv().cachedProperty,l_.fromPublic=function(t,e){return e instanceof l_?e:new l_(t,{pub:e})},l_.fromSecret=function(t,e){return e instanceof l_?e:new l_(t,{secret:e})},l_.prototype.secret=function(){return this._secret},u_(l_,"pubBytes",(function(){return this.eddsa.encodePoint(this.pub())})),u_(l_,"pub",(function(){return this._pubBytes?this.eddsa.decodePoint(this._pubBytes):this.eddsa.g.mul(this.priv())})),u_(l_,"privBytes",(function(){var t=this.eddsa,e=this.hash(),r=t.encodingLength-1,i=e.slice(0,t.encodingLength);return i[0]&=248,i[r]&=127,i[r]|=64,i})),u_(l_,"priv",(function(){return this.eddsa.decodeInt(this.privBytes())})),u_(l_,"hash",(function(){return this.eddsa.hash().update(this.secret()).digest()})),u_(l_,"messagePrefix",(function(){return this.hash().slice(this.eddsa.encodingLength)})),l_.prototype.sign=function(t){return a_(this._secret,"KeyPair can only verify"),this.eddsa.sign(t,this)},l_.prototype.verify=function(t,e){return this.eddsa.verify(t,e,this)},l_.prototype.getSecret=function(t){return a_(this._secret,"KeyPair is public only"),cv().encode(this.secret(),t)},l_.prototype.getPublic=function(t){return cv().encode(this.pubBytes(),t)},s_=l_),s_}var c_,p_,m_,g_,v_,b_=!1;function y_(t,e){this.eddsa=t,"object"!=typeof e&&(e=v_(e)),Array.isArray(e)&&(e={R:e.slice(0,t.encodingLength),S:e.slice(t.encodingLength)}),m_(e.R&&e.S,"Signature without R or S"),t.isPoint(e.R)&&(this._R=e.R),e.S instanceof p_&&(this._S=e.S),this._Rencoded=Array.isArray(e.R)?e.R:e.Rencoded,this._Sencoded=Array.isArray(e.S)?e.S:e.Sencoded}function w_(){return b_||(b_=!0,c_={},p_=Vg(),cv(),m_=cv().assert,g_=cv().cachedProperty,v_=cv().parseBytes,g_(y_,"S",(function(){return this.eddsa.decodeInt(this.Sencoded())})),g_(y_,"R",(function(){return this.eddsa.decodePoint(this.Rencoded())})),g_(y_,"Rencoded",(function(){return this.eddsa.encodePoint(this.R())})),g_(y_,"Sencoded",(function(){return this.eddsa.encodeInt(this.S())})),y_.prototype.toBytes=function(){return this.Rencoded().concat(this.Sencoded())},y_.prototype.toHex=function(){return cv().encode(this.toBytes(),"hex").toUpperCase()},c_=y_),c_}var M_,__,A_,E_,S_,k_=!1;function B_(t){if(A_("ed25519"===t,"only tested with ed25519 so far"),!(this instanceof B_))return new B_(t);t=__[t].curve,this.curve=t,this.g=t.g,this.g.precompute(t.n.bitLength()+1),this.pointClass=t.point().constructor,this.encodingLength=Math.ceil(t.n.bitLength()/8),this.hash=gM().sha512}function R_(){return k_||(k_=!0,M_={},gM(),__=BM(),cv(),A_=cv().assert,E_=cv().parseBytes,d_(),S_=w_(),M_=B_,B_.prototype.sign=function(t,e){t=E_(t);var r=this.keyFromSecret(e),i=this.hashInt(r.messagePrefix(),t),n=this.g.mul(i),o=this.encodePoint(n),s=this.hashInt(o,r.pubBytes(),t).mul(r.priv()),a=i.add(s).umod(this.curve.n);return this.makeSignature({R:n,S:a,Rencoded:o})},B_.prototype.verify=function(t,e,r){t=E_(t),e=this.makeSignature(e);var i=this.keyFromPublic(r),n=this.hashInt(e.Rencoded(),i.pubBytes(),t),o=this.g.mul(e.S());return e.R().add(i.pub().mul(n)).eq(o)},B_.prototype.hashInt=function(){for(var t=this.hash(),e=0;e=65&&r<=70?r-55:r>=97&&r<=102?r-87:r-48&15}function a(t,e,r){var i=s(t,r);return r-1>=e&&(i|=s(t,r-1)<<4),i}function h(t,e,r,i){for(var n=0,o=Math.min(t.length,r),s=e;s=49?a-49+10:a>=17?a-17+10:a}return n}n.isBN=function(t){return t instanceof n||null!==t&&"object"==typeof t&&t.constructor.wordSize===n.wordSize&&Array.isArray(t.words)},n.max=function(t,e){return t.cmp(e)>0?t:e},n.min=function(t,e){return t.cmp(e)<0?t:e},n.prototype._init=function(t,e,i){if("number"==typeof t)return this._initNumber(t,e,i);if("object"==typeof t)return this._initArray(t,e,i);"hex"===e&&(e=16),r(e===(0|e)&&e>=2&&e<=36);var n=0;"-"===(t=t.toString().replace(/\s+/g,""))[0]&&(n++,this.negative=1),n=0;n-=3)s=t[n]|t[n-1]<<8|t[n-2]<<16,this.words[o]|=s<>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===i)for(n=0,o=0;n>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this.strip()},n.prototype._parseHex=function(t,e,r){this.length=Math.ceil((t.length-e)/6),this.words=new Array(this.length);for(var i=0;i=e;i-=2)n=a(t,e,i)<=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;else for(i=(t.length-e)%2==0?e+1:e;i=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;this.strip()},n.prototype._parseBase=function(t,e,r){this.words=[0],this.length=1;for(var i=0,n=1;n<=67108863;n*=e)i++;i--,n=n/e|0;for(var o=t.length-r,s=o%i,a=Math.min(o,o-s)+r,u=0,f=r;f1&&0===this.words[this.length-1];)this.length--;return this._normSign()},n.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},n.prototype.inspect=function(){return(this.red?""};var u=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],f=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],l=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];function d(t,e,r){r.negative=e.negative^t.negative;var i=t.length+e.length|0;r.length=i,i=i-1|0;var n=0|t.words[0],o=0|e.words[0],s=n*o,a=67108863&s,h=s/67108864|0;r.words[0]=a;for(var u=1;u>>26,l=67108863&h,d=Math.min(u,e.length-1),c=Math.max(0,u-t.length+1);c<=d;c++){var p=u-c|0;f+=(s=(n=0|t.words[p])*(o=0|e.words[c])+l)/67108864|0,l=67108863&s}r.words[u]=0|l,h=0|f}return 0!==h?r.words[u]=0|h:r.length--,r.strip()}n.prototype.toString=function(t,e){var i;if(e=0|e||1,16===(t=t||10)||"hex"===t){i="";for(var n=0,o=0,s=0;s>>24-n&16777215)||s!==this.length-1?u[6-h.length]+h+i:h+i,(n+=2)>=26&&(n-=26,s--)}for(0!==o&&(i=o.toString(16)+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}if(t===(0|t)&&t>=2&&t<=36){var d=f[t],c=l[t];i="";var p=this.clone();for(p.negative=0;!p.isZero();){var m=p.modn(c).toString(t);i=(p=p.idivn(c)).isZero()?m+i:u[d-m.length]+m+i}for(this.isZero()&&(i="0"+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}r(!1,"Base should be between 2 and 36")},n.prototype.toNumber=function(){var t=this.words[0];return 2===this.length?t+=67108864*this.words[1]:3===this.length&&1===this.words[2]?t+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-t:t},n.prototype.toJSON=function(){return this.toString(16)},n.prototype.toBuffer=function(t,e){return r(void 0!==o),this.toArrayLike(o,t,e)},n.prototype.toArray=function(t,e){return this.toArrayLike(Array,t,e)},n.prototype.toArrayLike=function(t,e,i){var n=this.byteLength(),o=i||Math.max(1,n);r(n<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0"),this.strip();var s,a,h="le"===e,u=new t(o),f=this.clone();if(h){for(a=0;!f.isZero();a++)s=f.andln(255),f.iushrn(8),u[a]=s;for(;a=4096&&(r+=13,e>>>=13),e>=64&&(r+=7,e>>>=7),e>=8&&(r+=4,e>>>=4),e>=2&&(r+=2,e>>>=2),r+e},n.prototype._zeroBits=function(t){if(0===t)return 26;var e=t,r=0;return 0==(8191&e)&&(r+=13,e>>>=13),0==(127&e)&&(r+=7,e>>>=7),0==(15&e)&&(r+=4,e>>>=4),0==(3&e)&&(r+=2,e>>>=2),0==(1&e)&&r++,r},n.prototype.bitLength=function(){var t=this.words[this.length-1],e=this._countBits(t);return 26*(this.length-1)+e},n.prototype.zeroBits=function(){if(this.isZero())return 0;for(var t=0,e=0;et.length?this.clone().ior(t):t.clone().ior(this)},n.prototype.uor=function(t){return this.length>t.length?this.clone().iuor(t):t.clone().iuor(this)},n.prototype.iuand=function(t){var e;e=this.length>t.length?t:this;for(var r=0;rt.length?this.clone().iand(t):t.clone().iand(this)},n.prototype.uand=function(t){return this.length>t.length?this.clone().iuand(t):t.clone().iuand(this)},n.prototype.iuxor=function(t){var e,r;this.length>t.length?(e=this,r=t):(e=t,r=this);for(var i=0;it.length?this.clone().ixor(t):t.clone().ixor(this)},n.prototype.uxor=function(t){return this.length>t.length?this.clone().iuxor(t):t.clone().iuxor(this)},n.prototype.inotn=function(t){r("number"==typeof t&&t>=0);var e=0|Math.ceil(t/26),i=t%26;this._expand(e),i>0&&e--;for(var n=0;n0&&(this.words[n]=~this.words[n]&67108863>>26-i),this.strip()},n.prototype.notn=function(t){return this.clone().inotn(t)},n.prototype.setn=function(t,e){r("number"==typeof t&&t>=0);var i=t/26|0,n=t%26;return this._expand(i+1),this.words[i]=e?this.words[i]|1<t.length?(r=this,i=t):(r=t,i=this);for(var n=0,o=0;o>>26;for(;0!==n&&o>>26;if(this.length=r.length,0!==n)this.words[this.length]=n,this.length++;else if(r!==this)for(;ot.length?this.clone().iadd(t):t.clone().iadd(this)},n.prototype.isub=function(t){if(0!==t.negative){t.negative=0;var e=this.iadd(t);return t.negative=1,e._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(t),this.negative=1,this._normSign();var r,i,n=this.cmp(t);if(0===n)return this.negative=0,this.length=1,this.words[0]=0,this;n>0?(r=this,i=t):(r=t,i=this);for(var o=0,s=0;s>26,this.words[s]=67108863&e;for(;0!==o&&s>26,this.words[s]=67108863&e;if(0===o&&s>>13,c=0|s[1],p=8191&c,m=c>>>13,g=0|s[2],v=8191&g,b=g>>>13,y=0|s[3],w=8191&y,M=y>>>13,_=0|s[4],A=8191&_,E=_>>>13,S=0|s[5],k=8191&S,B=S>>>13,R=0|s[6],T=8191&R,x=R>>>13,I=0|s[7],C=8191&I,P=I>>>13,D=0|s[8],L=8191&D,O=D>>>13,j=0|s[9],U=8191&j,N=j>>>13,z=0|a[0],q=8191&z,F=z>>>13,Z=0|a[1],H=8191&Z,K=Z>>>13,W=0|a[2],G=8191&W,Y=W>>>13,V=0|a[3],$=8191&V,X=V>>>13,J=0|a[4],Q=8191&J,tt=J>>>13,et=0|a[5],rt=8191&et,it=et>>>13,nt=0|a[6],ot=8191&nt,st=nt>>>13,at=0|a[7],ht=8191&at,ut=at>>>13,ft=0|a[8],lt=8191&ft,dt=ft>>>13,ct=0|a[9],pt=8191&ct,mt=ct>>>13;r.negative=t.negative^e.negative,r.length=19;var gt=(u+(i=Math.imul(l,q))|0)+((8191&(n=(n=Math.imul(l,F))+Math.imul(d,q)|0))<<13)|0;u=((o=Math.imul(d,F))+(n>>>13)|0)+(gt>>>26)|0,gt&=67108863,i=Math.imul(p,q),n=(n=Math.imul(p,F))+Math.imul(m,q)|0,o=Math.imul(m,F);var vt=(u+(i=i+Math.imul(l,H)|0)|0)+((8191&(n=(n=n+Math.imul(l,K)|0)+Math.imul(d,H)|0))<<13)|0;u=((o=o+Math.imul(d,K)|0)+(n>>>13)|0)+(vt>>>26)|0,vt&=67108863,i=Math.imul(v,q),n=(n=Math.imul(v,F))+Math.imul(b,q)|0,o=Math.imul(b,F),i=i+Math.imul(p,H)|0,n=(n=n+Math.imul(p,K)|0)+Math.imul(m,H)|0,o=o+Math.imul(m,K)|0;var bt=(u+(i=i+Math.imul(l,G)|0)|0)+((8191&(n=(n=n+Math.imul(l,Y)|0)+Math.imul(d,G)|0))<<13)|0;u=((o=o+Math.imul(d,Y)|0)+(n>>>13)|0)+(bt>>>26)|0,bt&=67108863,i=Math.imul(w,q),n=(n=Math.imul(w,F))+Math.imul(M,q)|0,o=Math.imul(M,F),i=i+Math.imul(v,H)|0,n=(n=n+Math.imul(v,K)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,K)|0,i=i+Math.imul(p,G)|0,n=(n=n+Math.imul(p,Y)|0)+Math.imul(m,G)|0,o=o+Math.imul(m,Y)|0;var yt=(u+(i=i+Math.imul(l,$)|0)|0)+((8191&(n=(n=n+Math.imul(l,X)|0)+Math.imul(d,$)|0))<<13)|0;u=((o=o+Math.imul(d,X)|0)+(n>>>13)|0)+(yt>>>26)|0,yt&=67108863,i=Math.imul(A,q),n=(n=Math.imul(A,F))+Math.imul(E,q)|0,o=Math.imul(E,F),i=i+Math.imul(w,H)|0,n=(n=n+Math.imul(w,K)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,K)|0,i=i+Math.imul(v,G)|0,n=(n=n+Math.imul(v,Y)|0)+Math.imul(b,G)|0,o=o+Math.imul(b,Y)|0,i=i+Math.imul(p,$)|0,n=(n=n+Math.imul(p,X)|0)+Math.imul(m,$)|0,o=o+Math.imul(m,X)|0;var wt=(u+(i=i+Math.imul(l,Q)|0)|0)+((8191&(n=(n=n+Math.imul(l,tt)|0)+Math.imul(d,Q)|0))<<13)|0;u=((o=o+Math.imul(d,tt)|0)+(n>>>13)|0)+(wt>>>26)|0,wt&=67108863,i=Math.imul(k,q),n=(n=Math.imul(k,F))+Math.imul(B,q)|0,o=Math.imul(B,F),i=i+Math.imul(A,H)|0,n=(n=n+Math.imul(A,K)|0)+Math.imul(E,H)|0,o=o+Math.imul(E,K)|0,i=i+Math.imul(w,G)|0,n=(n=n+Math.imul(w,Y)|0)+Math.imul(M,G)|0,o=o+Math.imul(M,Y)|0,i=i+Math.imul(v,$)|0,n=(n=n+Math.imul(v,X)|0)+Math.imul(b,$)|0,o=o+Math.imul(b,X)|0,i=i+Math.imul(p,Q)|0,n=(n=n+Math.imul(p,tt)|0)+Math.imul(m,Q)|0,o=o+Math.imul(m,tt)|0;var Mt=(u+(i=i+Math.imul(l,rt)|0)|0)+((8191&(n=(n=n+Math.imul(l,it)|0)+Math.imul(d,rt)|0))<<13)|0;u=((o=o+Math.imul(d,it)|0)+(n>>>13)|0)+(Mt>>>26)|0,Mt&=67108863,i=Math.imul(T,q),n=(n=Math.imul(T,F))+Math.imul(x,q)|0,o=Math.imul(x,F),i=i+Math.imul(k,H)|0,n=(n=n+Math.imul(k,K)|0)+Math.imul(B,H)|0,o=o+Math.imul(B,K)|0,i=i+Math.imul(A,G)|0,n=(n=n+Math.imul(A,Y)|0)+Math.imul(E,G)|0,o=o+Math.imul(E,Y)|0,i=i+Math.imul(w,$)|0,n=(n=n+Math.imul(w,X)|0)+Math.imul(M,$)|0,o=o+Math.imul(M,X)|0,i=i+Math.imul(v,Q)|0,n=(n=n+Math.imul(v,tt)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,tt)|0,i=i+Math.imul(p,rt)|0,n=(n=n+Math.imul(p,it)|0)+Math.imul(m,rt)|0,o=o+Math.imul(m,it)|0;var _t=(u+(i=i+Math.imul(l,ot)|0)|0)+((8191&(n=(n=n+Math.imul(l,st)|0)+Math.imul(d,ot)|0))<<13)|0;u=((o=o+Math.imul(d,st)|0)+(n>>>13)|0)+(_t>>>26)|0,_t&=67108863,i=Math.imul(C,q),n=(n=Math.imul(C,F))+Math.imul(P,q)|0,o=Math.imul(P,F),i=i+Math.imul(T,H)|0,n=(n=n+Math.imul(T,K)|0)+Math.imul(x,H)|0,o=o+Math.imul(x,K)|0,i=i+Math.imul(k,G)|0,n=(n=n+Math.imul(k,Y)|0)+Math.imul(B,G)|0,o=o+Math.imul(B,Y)|0,i=i+Math.imul(A,$)|0,n=(n=n+Math.imul(A,X)|0)+Math.imul(E,$)|0,o=o+Math.imul(E,X)|0,i=i+Math.imul(w,Q)|0,n=(n=n+Math.imul(w,tt)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,tt)|0,i=i+Math.imul(v,rt)|0,n=(n=n+Math.imul(v,it)|0)+Math.imul(b,rt)|0,o=o+Math.imul(b,it)|0,i=i+Math.imul(p,ot)|0,n=(n=n+Math.imul(p,st)|0)+Math.imul(m,ot)|0,o=o+Math.imul(m,st)|0;var At=(u+(i=i+Math.imul(l,ht)|0)|0)+((8191&(n=(n=n+Math.imul(l,ut)|0)+Math.imul(d,ht)|0))<<13)|0;u=((o=o+Math.imul(d,ut)|0)+(n>>>13)|0)+(At>>>26)|0,At&=67108863,i=Math.imul(L,q),n=(n=Math.imul(L,F))+Math.imul(O,q)|0,o=Math.imul(O,F),i=i+Math.imul(C,H)|0,n=(n=n+Math.imul(C,K)|0)+Math.imul(P,H)|0,o=o+Math.imul(P,K)|0,i=i+Math.imul(T,G)|0,n=(n=n+Math.imul(T,Y)|0)+Math.imul(x,G)|0,o=o+Math.imul(x,Y)|0,i=i+Math.imul(k,$)|0,n=(n=n+Math.imul(k,X)|0)+Math.imul(B,$)|0,o=o+Math.imul(B,X)|0,i=i+Math.imul(A,Q)|0,n=(n=n+Math.imul(A,tt)|0)+Math.imul(E,Q)|0,o=o+Math.imul(E,tt)|0,i=i+Math.imul(w,rt)|0,n=(n=n+Math.imul(w,it)|0)+Math.imul(M,rt)|0,o=o+Math.imul(M,it)|0,i=i+Math.imul(v,ot)|0,n=(n=n+Math.imul(v,st)|0)+Math.imul(b,ot)|0,o=o+Math.imul(b,st)|0,i=i+Math.imul(p,ht)|0,n=(n=n+Math.imul(p,ut)|0)+Math.imul(m,ht)|0,o=o+Math.imul(m,ut)|0;var Et=(u+(i=i+Math.imul(l,lt)|0)|0)+((8191&(n=(n=n+Math.imul(l,dt)|0)+Math.imul(d,lt)|0))<<13)|0;u=((o=o+Math.imul(d,dt)|0)+(n>>>13)|0)+(Et>>>26)|0,Et&=67108863,i=Math.imul(U,q),n=(n=Math.imul(U,F))+Math.imul(N,q)|0,o=Math.imul(N,F),i=i+Math.imul(L,H)|0,n=(n=n+Math.imul(L,K)|0)+Math.imul(O,H)|0,o=o+Math.imul(O,K)|0,i=i+Math.imul(C,G)|0,n=(n=n+Math.imul(C,Y)|0)+Math.imul(P,G)|0,o=o+Math.imul(P,Y)|0,i=i+Math.imul(T,$)|0,n=(n=n+Math.imul(T,X)|0)+Math.imul(x,$)|0,o=o+Math.imul(x,X)|0,i=i+Math.imul(k,Q)|0,n=(n=n+Math.imul(k,tt)|0)+Math.imul(B,Q)|0,o=o+Math.imul(B,tt)|0,i=i+Math.imul(A,rt)|0,n=(n=n+Math.imul(A,it)|0)+Math.imul(E,rt)|0,o=o+Math.imul(E,it)|0,i=i+Math.imul(w,ot)|0,n=(n=n+Math.imul(w,st)|0)+Math.imul(M,ot)|0,o=o+Math.imul(M,st)|0,i=i+Math.imul(v,ht)|0,n=(n=n+Math.imul(v,ut)|0)+Math.imul(b,ht)|0,o=o+Math.imul(b,ut)|0,i=i+Math.imul(p,lt)|0,n=(n=n+Math.imul(p,dt)|0)+Math.imul(m,lt)|0,o=o+Math.imul(m,dt)|0;var St=(u+(i=i+Math.imul(l,pt)|0)|0)+((8191&(n=(n=n+Math.imul(l,mt)|0)+Math.imul(d,pt)|0))<<13)|0;u=((o=o+Math.imul(d,mt)|0)+(n>>>13)|0)+(St>>>26)|0,St&=67108863,i=Math.imul(U,H),n=(n=Math.imul(U,K))+Math.imul(N,H)|0,o=Math.imul(N,K),i=i+Math.imul(L,G)|0,n=(n=n+Math.imul(L,Y)|0)+Math.imul(O,G)|0,o=o+Math.imul(O,Y)|0,i=i+Math.imul(C,$)|0,n=(n=n+Math.imul(C,X)|0)+Math.imul(P,$)|0,o=o+Math.imul(P,X)|0,i=i+Math.imul(T,Q)|0,n=(n=n+Math.imul(T,tt)|0)+Math.imul(x,Q)|0,o=o+Math.imul(x,tt)|0,i=i+Math.imul(k,rt)|0,n=(n=n+Math.imul(k,it)|0)+Math.imul(B,rt)|0,o=o+Math.imul(B,it)|0,i=i+Math.imul(A,ot)|0,n=(n=n+Math.imul(A,st)|0)+Math.imul(E,ot)|0,o=o+Math.imul(E,st)|0,i=i+Math.imul(w,ht)|0,n=(n=n+Math.imul(w,ut)|0)+Math.imul(M,ht)|0,o=o+Math.imul(M,ut)|0,i=i+Math.imul(v,lt)|0,n=(n=n+Math.imul(v,dt)|0)+Math.imul(b,lt)|0,o=o+Math.imul(b,dt)|0;var kt=(u+(i=i+Math.imul(p,pt)|0)|0)+((8191&(n=(n=n+Math.imul(p,mt)|0)+Math.imul(m,pt)|0))<<13)|0;u=((o=o+Math.imul(m,mt)|0)+(n>>>13)|0)+(kt>>>26)|0,kt&=67108863,i=Math.imul(U,G),n=(n=Math.imul(U,Y))+Math.imul(N,G)|0,o=Math.imul(N,Y),i=i+Math.imul(L,$)|0,n=(n=n+Math.imul(L,X)|0)+Math.imul(O,$)|0,o=o+Math.imul(O,X)|0,i=i+Math.imul(C,Q)|0,n=(n=n+Math.imul(C,tt)|0)+Math.imul(P,Q)|0,o=o+Math.imul(P,tt)|0,i=i+Math.imul(T,rt)|0,n=(n=n+Math.imul(T,it)|0)+Math.imul(x,rt)|0,o=o+Math.imul(x,it)|0,i=i+Math.imul(k,ot)|0,n=(n=n+Math.imul(k,st)|0)+Math.imul(B,ot)|0,o=o+Math.imul(B,st)|0,i=i+Math.imul(A,ht)|0,n=(n=n+Math.imul(A,ut)|0)+Math.imul(E,ht)|0,o=o+Math.imul(E,ut)|0,i=i+Math.imul(w,lt)|0,n=(n=n+Math.imul(w,dt)|0)+Math.imul(M,lt)|0,o=o+Math.imul(M,dt)|0;var Bt=(u+(i=i+Math.imul(v,pt)|0)|0)+((8191&(n=(n=n+Math.imul(v,mt)|0)+Math.imul(b,pt)|0))<<13)|0;u=((o=o+Math.imul(b,mt)|0)+(n>>>13)|0)+(Bt>>>26)|0,Bt&=67108863,i=Math.imul(U,$),n=(n=Math.imul(U,X))+Math.imul(N,$)|0,o=Math.imul(N,X),i=i+Math.imul(L,Q)|0,n=(n=n+Math.imul(L,tt)|0)+Math.imul(O,Q)|0,o=o+Math.imul(O,tt)|0,i=i+Math.imul(C,rt)|0,n=(n=n+Math.imul(C,it)|0)+Math.imul(P,rt)|0,o=o+Math.imul(P,it)|0,i=i+Math.imul(T,ot)|0,n=(n=n+Math.imul(T,st)|0)+Math.imul(x,ot)|0,o=o+Math.imul(x,st)|0,i=i+Math.imul(k,ht)|0,n=(n=n+Math.imul(k,ut)|0)+Math.imul(B,ht)|0,o=o+Math.imul(B,ut)|0,i=i+Math.imul(A,lt)|0,n=(n=n+Math.imul(A,dt)|0)+Math.imul(E,lt)|0,o=o+Math.imul(E,dt)|0;var Rt=(u+(i=i+Math.imul(w,pt)|0)|0)+((8191&(n=(n=n+Math.imul(w,mt)|0)+Math.imul(M,pt)|0))<<13)|0;u=((o=o+Math.imul(M,mt)|0)+(n>>>13)|0)+(Rt>>>26)|0,Rt&=67108863,i=Math.imul(U,Q),n=(n=Math.imul(U,tt))+Math.imul(N,Q)|0,o=Math.imul(N,tt),i=i+Math.imul(L,rt)|0,n=(n=n+Math.imul(L,it)|0)+Math.imul(O,rt)|0,o=o+Math.imul(O,it)|0,i=i+Math.imul(C,ot)|0,n=(n=n+Math.imul(C,st)|0)+Math.imul(P,ot)|0,o=o+Math.imul(P,st)|0,i=i+Math.imul(T,ht)|0,n=(n=n+Math.imul(T,ut)|0)+Math.imul(x,ht)|0,o=o+Math.imul(x,ut)|0,i=i+Math.imul(k,lt)|0,n=(n=n+Math.imul(k,dt)|0)+Math.imul(B,lt)|0,o=o+Math.imul(B,dt)|0;var Tt=(u+(i=i+Math.imul(A,pt)|0)|0)+((8191&(n=(n=n+Math.imul(A,mt)|0)+Math.imul(E,pt)|0))<<13)|0;u=((o=o+Math.imul(E,mt)|0)+(n>>>13)|0)+(Tt>>>26)|0,Tt&=67108863,i=Math.imul(U,rt),n=(n=Math.imul(U,it))+Math.imul(N,rt)|0,o=Math.imul(N,it),i=i+Math.imul(L,ot)|0,n=(n=n+Math.imul(L,st)|0)+Math.imul(O,ot)|0,o=o+Math.imul(O,st)|0,i=i+Math.imul(C,ht)|0,n=(n=n+Math.imul(C,ut)|0)+Math.imul(P,ht)|0,o=o+Math.imul(P,ut)|0,i=i+Math.imul(T,lt)|0,n=(n=n+Math.imul(T,dt)|0)+Math.imul(x,lt)|0,o=o+Math.imul(x,dt)|0;var xt=(u+(i=i+Math.imul(k,pt)|0)|0)+((8191&(n=(n=n+Math.imul(k,mt)|0)+Math.imul(B,pt)|0))<<13)|0;u=((o=o+Math.imul(B,mt)|0)+(n>>>13)|0)+(xt>>>26)|0,xt&=67108863,i=Math.imul(U,ot),n=(n=Math.imul(U,st))+Math.imul(N,ot)|0,o=Math.imul(N,st),i=i+Math.imul(L,ht)|0,n=(n=n+Math.imul(L,ut)|0)+Math.imul(O,ht)|0,o=o+Math.imul(O,ut)|0,i=i+Math.imul(C,lt)|0,n=(n=n+Math.imul(C,dt)|0)+Math.imul(P,lt)|0,o=o+Math.imul(P,dt)|0;var It=(u+(i=i+Math.imul(T,pt)|0)|0)+((8191&(n=(n=n+Math.imul(T,mt)|0)+Math.imul(x,pt)|0))<<13)|0;u=((o=o+Math.imul(x,mt)|0)+(n>>>13)|0)+(It>>>26)|0,It&=67108863,i=Math.imul(U,ht),n=(n=Math.imul(U,ut))+Math.imul(N,ht)|0,o=Math.imul(N,ut),i=i+Math.imul(L,lt)|0,n=(n=n+Math.imul(L,dt)|0)+Math.imul(O,lt)|0,o=o+Math.imul(O,dt)|0;var Ct=(u+(i=i+Math.imul(C,pt)|0)|0)+((8191&(n=(n=n+Math.imul(C,mt)|0)+Math.imul(P,pt)|0))<<13)|0;u=((o=o+Math.imul(P,mt)|0)+(n>>>13)|0)+(Ct>>>26)|0,Ct&=67108863,i=Math.imul(U,lt),n=(n=Math.imul(U,dt))+Math.imul(N,lt)|0,o=Math.imul(N,dt);var Pt=(u+(i=i+Math.imul(L,pt)|0)|0)+((8191&(n=(n=n+Math.imul(L,mt)|0)+Math.imul(O,pt)|0))<<13)|0;u=((o=o+Math.imul(O,mt)|0)+(n>>>13)|0)+(Pt>>>26)|0,Pt&=67108863;var Dt=(u+(i=Math.imul(U,pt))|0)+((8191&(n=(n=Math.imul(U,mt))+Math.imul(N,pt)|0))<<13)|0;return u=((o=Math.imul(N,mt))+(n>>>13)|0)+(Dt>>>26)|0,Dt&=67108863,h[0]=gt,h[1]=vt,h[2]=bt,h[3]=yt,h[4]=wt,h[5]=Mt,h[6]=_t,h[7]=At,h[8]=Et,h[9]=St,h[10]=kt,h[11]=Bt,h[12]=Rt,h[13]=Tt,h[14]=xt,h[15]=It,h[16]=Ct,h[17]=Pt,h[18]=Dt,0!==u&&(h[19]=u,r.length++),r};function p(t,e,r){return(new m).mulp(t,e,r)}function m(t,e){this.x=t,this.y=e}Math.imul||(c=d),n.prototype.mulTo=function(t,e){var r,i=this.length+t.length;return r=10===this.length&&10===t.length?c(this,t,e):i<63?d(this,t,e):i<1024?function(t,e,r){r.negative=e.negative^t.negative,r.length=t.length+e.length;for(var i=0,n=0,o=0;o>>26)|0)>>>26,s&=67108863}r.words[o]=a,i=s,s=n}return 0!==i?r.words[o]=i:r.length--,r.strip()}(this,t,e):p(this,t,e),r},m.prototype.makeRBT=function(t){for(var e=new Array(t),r=n.prototype._countBits(t)-1,i=0;i>=1;return i},m.prototype.permute=function(t,e,r,i,n,o){for(var s=0;s>>=1)n++;return 1<>>=13,i[2*s+1]=8191&o,o>>>=13;for(s=2*e;s>=26,e+=n/67108864|0,e+=o>>>26,this.words[i]=67108863&o}return 0!==e&&(this.words[i]=e,this.length++),this},n.prototype.muln=function(t){return this.clone().imuln(t)},n.prototype.sqr=function(){return this.mul(this)},n.prototype.isqr=function(){return this.imul(this.clone())},n.prototype.pow=function(t){var e=function(t){for(var e=new Array(t.bitLength()),r=0;r>>n}return e}(t);if(0===e.length)return new n(1);for(var r=this,i=0;i=0);var e,i=t%26,n=(t-i)/26,o=67108863>>>26-i<<26-i;if(0!==i){var s=0;for(e=0;e>>26-i}s&&(this.words[e]=s,this.length++)}if(0!==n){for(e=this.length-1;e>=0;e--)this.words[e+n]=this.words[e];for(e=0;e=0),n=e?(e-e%26)/26:0;var o=t%26,s=Math.min((t-o)/26,this.length),a=67108863^67108863>>>o<s)for(this.length-=s,u=0;u=0&&(0!==f||u>=n);u--){var l=0|this.words[u];this.words[u]=f<<26-o|l>>>o,f=l&a}return h&&0!==f&&(h.words[h.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},n.prototype.ishrn=function(t,e,i){return r(0===this.negative),this.iushrn(t,e,i)},n.prototype.shln=function(t){return this.clone().ishln(t)},n.prototype.ushln=function(t){return this.clone().iushln(t)},n.prototype.shrn=function(t){return this.clone().ishrn(t)},n.prototype.ushrn=function(t){return this.clone().iushrn(t)},n.prototype.testn=function(t){r("number"==typeof t&&t>=0);var e=t%26,i=(t-e)/26,n=1<=0);var e=t%26,i=(t-e)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=i)return this;if(0!==e&&i++,this.length=Math.min(i,this.length),0!==e){var n=67108863^67108863>>>e<=67108864;e++)this.words[e]-=67108864,e===this.length-1?this.words[e+1]=1:this.words[e+1]++;return this.length=Math.max(this.length,e+1),this},n.prototype.isubn=function(t){if(r("number"==typeof t),r(t<67108864),t<0)return this.iaddn(-t);if(0!==this.negative)return this.negative=0,this.iaddn(t),this.negative=1,this;if(this.words[0]-=t,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var e=0;e>26)-(h/67108864|0),this.words[n+i]=67108863&o}for(;n>26,this.words[n+i]=67108863&o;if(0===a)return this.strip();for(r(-1===a),a=0,n=0;n>26,this.words[n]=67108863&o;return this.negative=1,this.strip()},n.prototype._wordDiv=function(t,e){var r=(this.length,t.length),i=this.clone(),o=t,s=0|o.words[o.length-1];0!==(r=26-this._countBits(s))&&(o=o.ushln(r),i.iushln(r),s=0|o.words[o.length-1]);var a,h=i.length-o.length;if("mod"!==e){(a=new n(null)).length=h+1,a.words=new Array(a.length);for(var u=0;u=0;l--){var d=67108864*(0|i.words[o.length+l])+(0|i.words[o.length+l-1]);for(d=Math.min(d/s|0,67108863),i._ishlnsubmul(o,d,l);0!==i.negative;)d--,i.negative=0,i._ishlnsubmul(o,1,l),i.isZero()||(i.negative^=1);a&&(a.words[l]=d)}return a&&a.strip(),i.strip(),"div"!==e&&0!==r&&i.iushrn(r),{div:a||null,mod:i}},n.prototype.divmod=function(t,e,i){return r(!t.isZero()),this.isZero()?{div:new n(0),mod:new n(0)}:0!==this.negative&&0===t.negative?(a=this.neg().divmod(t,e),"mod"!==e&&(o=a.div.neg()),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.iadd(t)),{div:o,mod:s}):0===this.negative&&0!==t.negative?(a=this.divmod(t.neg(),e),"mod"!==e&&(o=a.div.neg()),{div:o,mod:a.mod}):0!=(this.negative&t.negative)?(a=this.neg().divmod(t.neg(),e),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.isub(t)),{div:a.div,mod:s}):t.length>this.length||this.cmp(t)<0?{div:new n(0),mod:this}:1===t.length?"div"===e?{div:this.divn(t.words[0]),mod:null}:"mod"===e?{div:null,mod:new n(this.modn(t.words[0]))}:{div:this.divn(t.words[0]),mod:new n(this.modn(t.words[0]))}:this._wordDiv(t,e);var o,s,a},n.prototype.div=function(t){return this.divmod(t,"div",!1).div},n.prototype.mod=function(t){return this.divmod(t,"mod",!1).mod},n.prototype.umod=function(t){return this.divmod(t,"mod",!0).mod},n.prototype.divRound=function(t){var e=this.divmod(t);if(e.mod.isZero())return e.div;var r=0!==e.div.negative?e.mod.isub(t):e.mod,i=t.ushrn(1),n=t.andln(1),o=r.cmp(i);return o<0||1===n&&0===o?e.div:0!==e.div.negative?e.div.isubn(1):e.div.iaddn(1)},n.prototype.modn=function(t){r(t<=67108863);for(var e=(1<<26)%t,i=0,n=this.length-1;n>=0;n--)i=(e*i+(0|this.words[n]))%t;return i},n.prototype.idivn=function(t){r(t<=67108863);for(var e=0,i=this.length-1;i>=0;i--){var n=(0|this.words[i])+67108864*e;this.words[i]=n/t|0,e=n%t}return this.strip()},n.prototype.divn=function(t){return this.clone().idivn(t)},n.prototype.egcd=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o=new n(1),s=new n(0),a=new n(0),h=new n(1),u=0;e.isEven()&&i.isEven();)e.iushrn(1),i.iushrn(1),++u;for(var f=i.clone(),l=e.clone();!e.isZero();){for(var d=0,c=1;0==(e.words[0]&c)&&d<26;++d,c<<=1);if(d>0)for(e.iushrn(d);d-- >0;)(o.isOdd()||s.isOdd())&&(o.iadd(f),s.isub(l)),o.iushrn(1),s.iushrn(1);for(var p=0,m=1;0==(i.words[0]&m)&&p<26;++p,m<<=1);if(p>0)for(i.iushrn(p);p-- >0;)(a.isOdd()||h.isOdd())&&(a.iadd(f),h.isub(l)),a.iushrn(1),h.iushrn(1);e.cmp(i)>=0?(e.isub(i),o.isub(a),s.isub(h)):(i.isub(e),a.isub(o),h.isub(s))}return{a:a,b:h,gcd:i.iushln(u)}},n.prototype._invmp=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o,s=new n(1),a=new n(0),h=i.clone();e.cmpn(1)>0&&i.cmpn(1)>0;){for(var u=0,f=1;0==(e.words[0]&f)&&u<26;++u,f<<=1);if(u>0)for(e.iushrn(u);u-- >0;)s.isOdd()&&s.iadd(h),s.iushrn(1);for(var l=0,d=1;0==(i.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(i.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(h),a.iushrn(1);e.cmp(i)>=0?(e.isub(i),s.isub(a)):(i.isub(e),a.isub(s))}return(o=0===e.cmpn(1)?s:a).cmpn(0)<0&&o.iadd(t),o},n.prototype.gcd=function(t){if(this.isZero())return t.abs();if(t.isZero())return this.abs();var e=this.clone(),r=t.clone();e.negative=0,r.negative=0;for(var i=0;e.isEven()&&r.isEven();i++)e.iushrn(1),r.iushrn(1);for(;;){for(;e.isEven();)e.iushrn(1);for(;r.isEven();)r.iushrn(1);var n=e.cmp(r);if(n<0){var o=e;e=r,r=o}else if(0===n||0===r.cmpn(1))break;e.isub(r)}return r.iushln(i)},n.prototype.invm=function(t){return this.egcd(t).a.umod(t)},n.prototype.isEven=function(){return 0==(1&this.words[0])},n.prototype.isOdd=function(){return 1==(1&this.words[0])},n.prototype.andln=function(t){return this.words[0]&t},n.prototype.bincn=function(t){r("number"==typeof t);var e=t%26,i=(t-e)/26,n=1<>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},n.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},n.prototype.cmpn=function(t){var e,i=t<0;if(0!==this.negative&&!i)return-1;if(0===this.negative&&i)return 1;if(this.strip(),this.length>1)e=1;else{i&&(t=-t),r(t<=67108863,"Number is too big");var n=0|this.words[0];e=n===t?0:nt.length)return 1;if(this.length=0;r--){var i=0|this.words[r],n=0|t.words[r];if(i!==n){in&&(e=1);break}}return e},n.prototype.gtn=function(t){return 1===this.cmpn(t)},n.prototype.gt=function(t){return 1===this.cmp(t)},n.prototype.gten=function(t){return this.cmpn(t)>=0},n.prototype.gte=function(t){return this.cmp(t)>=0},n.prototype.ltn=function(t){return-1===this.cmpn(t)},n.prototype.lt=function(t){return-1===this.cmp(t)},n.prototype.lten=function(t){return this.cmpn(t)<=0},n.prototype.lte=function(t){return this.cmp(t)<=0},n.prototype.eqn=function(t){return 0===this.cmpn(t)},n.prototype.eq=function(t){return 0===this.cmp(t)},n.red=function(t){return new _(t)},n.prototype.toRed=function(t){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),t.convertTo(this)._forceRed(t)},n.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},n.prototype._forceRed=function(t){return this.red=t,this},n.prototype.forceRed=function(t){return r(!this.red,"Already a number in reduction context"),this._forceRed(t)},n.prototype.redAdd=function(t){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,t)},n.prototype.redIAdd=function(t){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,t)},n.prototype.redSub=function(t){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,t)},n.prototype.redISub=function(t){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,t)},n.prototype.redShl=function(t){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,t)},n.prototype.redMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.mul(this,t)},n.prototype.redIMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.imul(this,t)},n.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},n.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},n.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},n.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},n.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},n.prototype.redPow=function(t){return r(this.red&&!t.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,t)};var g={k256:null,p224:null,p192:null,p25519:null};function v(t,e){this.name=t,this.p=new n(e,16),this.n=this.p.bitLength(),this.k=new n(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function b(){v.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function y(){v.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function w(){v.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function M(){v.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function _(t){if("string"==typeof t){var e=n._prime(t);this.m=e.p,this.prime=e}else r(t.gtn(1),"modulus must be greater than 1"),this.m=t,this.prime=null}function A(t){_.call(this,t),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new n(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}v.prototype._tmp=function(){var t=new n(null);return t.words=new Array(Math.ceil(this.n/13)),t},v.prototype.ireduce=function(t){var e,r=t;do{this.split(r,this.tmp),e=(r=(r=this.imulK(r)).iadd(this.tmp)).bitLength()}while(e>this.n);var i=e0?r.isub(this.p):void 0!==r.strip?r.strip():r._strip(),r},v.prototype.split=function(t,e){t.iushrn(this.n,0,e)},v.prototype.imulK=function(t){return t.imul(this.k)},i(b,v),b.prototype.split=function(t,e){for(var r=4194303,i=Math.min(t.length,9),n=0;n>>22,o=s}o>>>=22,t.words[n-10]=o,0===o&&t.length>10?t.length-=10:t.length-=9},b.prototype.imulK=function(t){t.words[t.length]=0,t.words[t.length+1]=0,t.length+=2;for(var e=0,r=0;r>>=26,t.words[r]=n,e=i}return 0!==e&&(t.words[t.length++]=e),t},n._prime=function(t){if(g[t])return g[t];var e;if("k256"===t)e=new b;else if("p224"===t)e=new y;else if("p192"===t)e=new w;else{if("p25519"!==t)throw new Error("Unknown prime "+t);e=new M}return g[t]=e,e},_.prototype._verify1=function(t){r(0===t.negative,"red works only with positives"),r(t.red,"red works only with red numbers")},_.prototype._verify2=function(t,e){r(0==(t.negative|e.negative),"red works only with positives"),r(t.red&&t.red===e.red,"red works only with red numbers")},_.prototype.imod=function(t){return this.prime?this.prime.ireduce(t)._forceRed(this):t.umod(this.m)._forceRed(this)},_.prototype.neg=function(t){return t.isZero()?t.clone():this.m.sub(t)._forceRed(this)},_.prototype.add=function(t,e){this._verify2(t,e);var r=t.add(e);return r.cmp(this.m)>=0&&r.isub(this.m),r._forceRed(this)},_.prototype.iadd=function(t,e){this._verify2(t,e);var r=t.iadd(e);return r.cmp(this.m)>=0&&r.isub(this.m),r},_.prototype.sub=function(t,e){this._verify2(t,e);var r=t.sub(e);return r.cmpn(0)<0&&r.iadd(this.m),r._forceRed(this)},_.prototype.isub=function(t,e){this._verify2(t,e);var r=t.isub(e);return r.cmpn(0)<0&&r.iadd(this.m),r},_.prototype.shl=function(t,e){return this._verify1(t),this.imod(t.ushln(e))},_.prototype.imul=function(t,e){return this._verify2(t,e),this.imod(t.imul(e))},_.prototype.mul=function(t,e){return this._verify2(t,e),this.imod(t.mul(e))},_.prototype.isqr=function(t){return this.imul(t,t.clone())},_.prototype.sqr=function(t){return this.mul(t,t)},_.prototype.sqrt=function(t){if(t.isZero())return t.clone();var e=this.m.andln(3);if(r(e%2==1),3===e){var i=this.m.add(new n(1)).iushrn(2);return this.pow(t,i)}for(var o=this.m.subn(1),s=0;!o.isZero()&&0===o.andln(1);)s++,o.iushrn(1);r(!o.isZero());var a=new n(1).toRed(this),h=a.redNeg(),u=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new n(2*f*f).toRed(this);0!==this.pow(f,u).cmp(h);)f.redIAdd(h);for(var l=this.pow(f,o),d=this.pow(t,o.addn(1).iushrn(1)),c=this.pow(t,o),p=s;0!==c.cmp(a);){for(var m=c,g=0;0!==m.cmp(a);g++)m=m.redSqr();r(g=0;i--){for(var u=e.words[i],f=h-1;f>=0;f--){var l=u>>f&1;o!==r[0]&&(o=this.sqr(o)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===i&&0===f)&&(o=this.mul(o,r[s]),a=0,s=0)):a=0}h=26}return o},_.prototype.convertTo=function(t){var e=t.umod(this.m);return e===t?e.clone():e},_.prototype.convertFrom=function(t){var e=t.clone();return e.red=null,e},n.mont=function(t){return new A(t)},i(A,_),A.prototype.convertTo=function(t){return this.imod(t.ushln(this.shift))},A.prototype.convertFrom=function(t){var e=this.imod(t.mul(this.rinv));return e.red=null,e},A.prototype.imul=function(t,e){if(t.isZero()||e.isZero())return t.words[0]=0,t.length=1,t;var r=t.imul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),n=r.isub(i).iushrn(this.shift),o=n;return n.cmp(this.m)>=0?o=n.isub(this.m):n.cmpn(0)<0&&(o=n.iadd(this.m)),o._forceRed(this)},A.prototype.mul=function(t,e){if(t.isZero()||e.isZero())return new n(0)._forceRed(this);var r=t.mul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),o=r.isub(i).iushrn(this.shift),s=o;return o.cmp(this.m)>=0?s=o.isub(this.m):o.cmpn(0)<0&&(s=o.iadd(this.m)),s._forceRed(this)},A.prototype.invm=function(t){return this.imod(t._invmp(this.m).mul(this.r2))._forceRed(this)}}(void 0===t||t,this),t.exports}.call({})}function O_(){return D_||(D_=!0,L_()),P_}var j_,U_,N_,z_,q_,F_,Z_=!1;function H_(){return Z_||(Z_=!0,function(){for(q_ in j_={},jt(),U_=Ye(),N_=U_.Buffer,z_={},U_)U_.hasOwnProperty(q_)&&"SlowBuffer"!==q_&&"Buffer"!==q_&&(z_[q_]=U_[q_]);for(q_ in F_=z_.Buffer={},N_)N_.hasOwnProperty(q_)&&"allocUnsafe"!==q_&&"allocUnsafeSlow"!==q_&&(F_[q_]=N_[q_]);if(z_.Buffer.prototype=N_.prototype,F_.from&&F_.from!==Uint8Array.from||(F_.from=function(t,e,r){if("number"==typeof t)throw new TypeError('The "value" argument must not be of type number. Received type '+typeof t);if(t&&void 0===t.length)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);return N_(t,e,r)}),F_.alloc||(F_.alloc=function(t,e,r){if("number"!=typeof t)throw new TypeError('The "size" argument must be of type number. Received type '+typeof t);if(t<0||t>=2*(1<<30))throw new RangeError('The value "'+t+'" is invalid for option "size"');var i=N_(t);return e&&0!==e.length?"string"==typeof r?i.fill(e,r):i.fill(e):i.fill(0),i}),!z_.kStringMaxLength)try{z_.kStringMaxLength=jt().binding("buffer").kStringMaxLength}catch(t){}z_.constants||(z_.constants={MAX_LENGTH:z_.kMaxLength},z_.kStringMaxLength&&(z_.constants.MAX_STRING_LENGTH=z_.kStringMaxLength)),j_=z_}()),j_}var K_,W_,G_,Y_=!1;function V_(t){this._reporterState={obj:null,path:[],options:t||{},errors:[]}}function $_(t,e){this.path=t,this.rethrow(e)}function X_(){return Y_||(Y_=!0,K_={},W_=Ar(),G_=V_,K_.Reporter=G_,V_.prototype.isError=function(t){return t instanceof $_},V_.prototype.save=function(){const t=this._reporterState;return{obj:t.obj,pathLen:t.path.length}},V_.prototype.restore=function(t){const e=this._reporterState;e.obj=t.obj,e.path=e.path.slice(0,t.pathLen)},V_.prototype.enterKey=function(t){return this._reporterState.path.push(t)},V_.prototype.exitKey=function(t){const e=this._reporterState;e.path=e.path.slice(0,t-1)},V_.prototype.leaveKey=function(t,e,r){const i=this._reporterState;this.exitKey(t),null!==i.obj&&(i.obj[e]=r)},V_.prototype.path=function(){return this._reporterState.path.join("/")},V_.prototype.enterObject=function(){const t=this._reporterState,e=t.obj;return t.obj={},e},V_.prototype.leaveObject=function(t){const e=this._reporterState,r=e.obj;return e.obj=t,r},V_.prototype.error=function(t){let e;const r=this._reporterState,i=t instanceof $_;if(e=i?t:new $_(r.path.map((function(t){return"["+JSON.stringify(t)+"]"})).join(""),t.message||t,t.stack),!r.options.partial)throw e;return i||r.errors.push(e),e},V_.prototype.wrapResult=function(t){const e=this._reporterState;return e.options.partial?{result:this.isError(t)?null:t,errors:e.errors}:t},W_($_,Error),$_.prototype.rethrow=function(t){if(this.message=t+" at: "+(this.path||"(shallow)"),Error.captureStackTrace&&Error.captureStackTrace(this,$_),!this.stack)try{throw new Error(this.message)}catch(t){this.stack=t.stack}return this}),K_}var J_,Q_,tA,eA,rA,iA,nA=!1;function oA(t,e){tA.call(this,e),eA.isBuffer(t)?(this.base=t,this.offset=0,this.length=t.length):this.error("Input not Buffer")}function sA(t,e){if(Array.isArray(t))this.length=0,this.value=t.map((function(t){return sA.isEncoderBuffer(t)||(t=new sA(t,e)),this.length+=t.length,t}),this);else if("number"==typeof t){if(!(0<=t&&t<=255))return e.error("non-byte EncoderBuffer value");this.value=t,this.length=1}else if("string"==typeof t)this.value=t,this.length=eA.byteLength(t);else{if(!eA.isBuffer(t))return e.error("Unsupported type: "+typeof t);this.value=t,this.length=t.length}}function aA(){return nA||(nA=!0,J_={},Q_=Ar(),tA=X_().Reporter,eA=H_().Buffer,Q_(oA,tA),rA=oA,J_.DecoderBuffer=rA,oA.isDecoderBuffer=function(t){return t instanceof oA||"object"==typeof t&&eA.isBuffer(t.base)&&"DecoderBuffer"===t.constructor.name&&"number"==typeof t.offset&&"number"==typeof t.length&&"function"==typeof t.save&&"function"==typeof t.restore&&"function"==typeof t.isEmpty&&"function"==typeof t.readUInt8&&"function"==typeof t.skip&&"function"==typeof t.raw},oA.prototype.save=function(){return{offset:this.offset,reporter:tA.prototype.save.call(this)}},oA.prototype.restore=function(t){const e=new oA(this.base);return e.offset=t.offset,e.length=this.offset,this.offset=t.offset,tA.prototype.restore.call(this,t.reporter),e},oA.prototype.isEmpty=function(){return this.offset===this.length},oA.prototype.readUInt8=function(t){return this.offset+1<=this.length?this.base.readUInt8(this.offset++,!0):this.error(t||"DecoderBuffer overrun")},oA.prototype.skip=function(t,e){if(!(this.offset+t<=this.length))return this.error(e||"DecoderBuffer overrun");const r=new oA(this.base);return r._reporterState=this._reporterState,r.offset=this.offset,r.length=this.offset+t,this.offset+=t,r},oA.prototype.raw=function(t){return this.base.slice(t?t.offset:this.offset,this.length)},iA=sA,J_.EncoderBuffer=iA,sA.isEncoderBuffer=function(t){return t instanceof sA||"object"==typeof t&&"EncoderBuffer"===t.constructor.name&&"number"==typeof t.length&&"function"==typeof t.join},sA.prototype.join=function(t,e){return t||(t=eA.alloc(this.length)),e||(e=0),0===this.length||(Array.isArray(this.value)?this.value.forEach((function(r){r.join(t,e),e+=r.length})):("number"==typeof this.value?t[e]=this.value:"string"==typeof this.value?t.write(this.value,e):eA.isBuffer(this.value)&&this.value.copy(t,e),e+=this.length)),t}),J_}var hA,uA,fA,lA,dA,cA,pA,mA,gA,vA=!1;function bA(t,e,r){const i={};this._baseState=i,i.name=r,i.enc=t,i.parent=e||null,i.children=null,i.tag=null,i.args=null,i.reverseArgs=null,i.choice=null,i.optional=!1,i.any=!1,i.obj=!1,i.use=null,i.useDecoder=null,i.key=null,i.default=null,i.explicit=null,i.implicit=null,i.contains=null,i.parent||(i.children=[],this._wrap())}function yA(){return vA||(vA=!0,hA={},uA=X_().Reporter,fA=aA().EncoderBuffer,lA=aA().DecoderBuffer,dA=el(),pA=["key","obj","use","optional","explicit","implicit","def","choice","any","contains"].concat(cA=["seq","seqof","set","setof","objid","bool","gentime","utctime","null_","enum","int","objDesc","bitstr","bmpstr","charstr","genstr","graphstr","ia5str","iso646str","numstr","octstr","printstr","t61str","unistr","utf8str","videostr"]),mA=["_peekTag","_decodeTag","_use","_decodeStr","_decodeObjid","_decodeTime","_decodeNull","_decodeInt","_decodeBool","_decodeList","_encodeComposite","_encodeStr","_encodeObjid","_encodeTime","_encodeNull","_encodeInt","_encodeBool"],hA=bA,gA=["enc","parent","children","tag","args","reverseArgs","choice","optional","any","obj","use","alteredUse","key","default","explicit","implicit","contains"],bA.prototype.clone=function(){const t=this._baseState,e={};gA.forEach((function(r){e[r]=t[r]}));const r=new this.constructor(e.parent);return r._baseState=e,r},bA.prototype._wrap=function(){const t=this._baseState;pA.forEach((function(e){this[e]=function(){const r=new this.constructor(this);return t.children.push(r),r[e].apply(r,arguments)}}),this)},bA.prototype._init=function(t){const e=this._baseState;dA(null===e.parent),t.call(this),e.children=e.children.filter((function(t){return t._baseState.parent===this}),this),dA.equal(e.children.length,1,"Root node can have only one child")},bA.prototype._useArgs=function(t){const e=this._baseState,r=t.filter((function(t){return t instanceof this.constructor}),this);t=t.filter((function(t){return!(t instanceof this.constructor)}),this),0!==r.length&&(dA(null===e.children),e.children=r,r.forEach((function(t){t._baseState.parent=this}),this)),0!==t.length&&(dA(null===e.args),e.args=t,e.reverseArgs=t.map((function(t){if("object"!=typeof t||t.constructor!==Object)return t;const e={};return Object.keys(t).forEach((function(r){r==(0|r)&&(r|=0);const i=t[r];e[i]=r})),e})))},mA.forEach((function(t){bA.prototype[t]=function(){const e=this._baseState;throw new Error(t+" not implemented for encoding: "+e.enc)}})),cA.forEach((function(t){bA.prototype[t]=function(){const e=this._baseState,r=Array.prototype.slice.call(arguments);return dA(null===e.tag),e.tag=t,this._useArgs(r),this}})),bA.prototype.use=function(t){dA(t);const e=this._baseState;return dA(null===e.use),e.use=t,this},bA.prototype.optional=function(){return this._baseState.optional=!0,this},bA.prototype.def=function(t){const e=this._baseState;return dA(null===e.default),e.default=t,e.optional=!0,this},bA.prototype.explicit=function(t){const e=this._baseState;return dA(null===e.explicit&&null===e.implicit),e.explicit=t,this},bA.prototype.implicit=function(t){const e=this._baseState;return dA(null===e.explicit&&null===e.implicit),e.implicit=t,this},bA.prototype.obj=function(){const t=this._baseState,e=Array.prototype.slice.call(arguments);return t.obj=!0,0!==e.length&&this._useArgs(e),this},bA.prototype.key=function(t){const e=this._baseState;return dA(null===e.key),e.key=t,this},bA.prototype.any=function(){return this._baseState.any=!0,this},bA.prototype.choice=function(t){const e=this._baseState;return dA(null===e.choice),e.choice=t,this._useArgs(Object.keys(t).map((function(e){return t[e]}))),this},bA.prototype.contains=function(t){const e=this._baseState;return dA(null===e.use),e.contains=t,this},bA.prototype._decode=function(t,e){const r=this._baseState;if(null===r.parent)return t.wrapResult(r.children[0]._decode(t,e));let i,n=r.default,o=!0,s=null;if(null!==r.key&&(s=t.enterKey(r.key)),r.optional){let i=null;if(null!==r.explicit?i=r.explicit:null!==r.implicit?i=r.implicit:null!==r.tag&&(i=r.tag),null!==i||r.any){if(o=this._peekTag(t,i,r.any),t.isError(o))return o}else{const i=t.save();try{null===r.choice?this._decodeGeneric(r.tag,t,e):this._decodeChoice(t,e),o=!0}catch(t){o=!1}t.restore(i)}}if(r.obj&&o&&(i=t.enterObject()),o){if(null!==r.explicit){const e=this._decodeTag(t,r.explicit);if(t.isError(e))return e;t=e}const i=t.offset;if(null===r.use&&null===r.choice){let e;r.any&&(e=t.save());const i=this._decodeTag(t,null!==r.implicit?r.implicit:r.tag,r.any);if(t.isError(i))return i;r.any?n=t.raw(e):t=i}if(e&&e.track&&null!==r.tag&&e.track(t.path(),i,t.length,"tagged"),e&&e.track&&null!==r.tag&&e.track(t.path(),t.offset,t.length,"content"),r.any||(n=null===r.choice?this._decodeGeneric(r.tag,t,e):this._decodeChoice(t,e)),t.isError(n))return n;if(r.any||null!==r.choice||null===r.children||r.children.forEach((function(r){r._decode(t,e)})),r.contains&&("octstr"===r.tag||"bitstr"===r.tag)){const i=new lA(n);n=this._getUse(r.contains,t._reporterState.obj)._decode(i,e)}}return r.obj&&o&&(n=t.leaveObject(i)),null===r.key||null===n&&!0!==o?null!==s&&t.exitKey(s):t.leaveKey(s,r.key,n),n},bA.prototype._decodeGeneric=function(t,e,r){const i=this._baseState;return"seq"===t||"set"===t?null:"seqof"===t||"setof"===t?this._decodeList(e,t,i.args[0],r):/str$/.test(t)?this._decodeStr(e,t,r):"objid"===t&&i.args?this._decodeObjid(e,i.args[0],i.args[1],r):"objid"===t?this._decodeObjid(e,null,null,r):"gentime"===t||"utctime"===t?this._decodeTime(e,t,r):"null_"===t?this._decodeNull(e,r):"bool"===t?this._decodeBool(e,r):"objDesc"===t?this._decodeStr(e,t,r):"int"===t||"enum"===t?this._decodeInt(e,i.args&&i.args[0],r):null!==i.use?this._getUse(i.use,e._reporterState.obj)._decode(e,r):e.error("unknown tag: "+t)},bA.prototype._getUse=function(t,e){const r=this._baseState;return r.useDecoder=this._use(t,e),dA(null===r.useDecoder._baseState.parent),r.useDecoder=r.useDecoder._baseState.children[0],r.implicit!==r.useDecoder._baseState.implicit&&(r.useDecoder=r.useDecoder.clone(),r.useDecoder._baseState.implicit=r.implicit),r.useDecoder},bA.prototype._decodeChoice=function(t,e){const r=this._baseState;let i=null,n=!1;return Object.keys(r.choice).some((function(o){const s=t.save(),a=r.choice[o];try{const r=a._decode(t,e);if(t.isError(r))return!1;i={type:o,value:r},n=!0}catch(e){return t.restore(s),!1}return!0}),this),n?i:t.error("Choice not matched")},bA.prototype._createEncoderBuffer=function(t){return new fA(t,this.reporter)},bA.prototype._encode=function(t,e,r){const i=this._baseState;if(null!==i.default&&i.default===t)return;const n=this._encodeValue(t,e,r);return void 0===n||this._skipDefault(n,e,r)?void 0:n},bA.prototype._encodeValue=function(t,e,r){const i=this._baseState;if(null===i.parent)return i.children[0]._encode(t,e||new uA);let n=null;if(this.reporter=e,i.optional&&void 0===t){if(null===i.default)return;t=i.default}let o=null,s=!1;if(i.any)n=this._createEncoderBuffer(t);else if(i.choice)n=this._encodeChoice(t,e);else if(i.contains)o=this._getUse(i.contains,r)._encode(t,e),s=!0;else if(i.children)o=i.children.map((function(r){if("null_"===r._baseState.tag)return r._encode(null,e,t);if(null===r._baseState.key)return e.error("Child should have a key");const i=e.enterKey(r._baseState.key);if("object"!=typeof t)return e.error("Child expected, but input is not object");const n=r._encode(t[r._baseState.key],e,t);return e.leaveKey(i),n}),this).filter((function(t){return t})),o=this._createEncoderBuffer(o);else if("seqof"===i.tag||"setof"===i.tag){if(!i.args||1!==i.args.length)return e.error("Too many args for : "+i.tag);if(!Array.isArray(t))return e.error("seqof/setof, but data is not Array");const r=this.clone();r._baseState.implicit=null,o=this._createEncoderBuffer(t.map((function(r){const i=this._baseState;return this._getUse(i.args[0],t)._encode(r,e)}),r))}else null!==i.use?n=this._getUse(i.use,r)._encode(t,e):(o=this._encodePrimitive(i.tag,t),s=!0);if(!i.any&&null===i.choice){const t=null!==i.implicit?i.implicit:i.tag,r=null===i.implicit?"universal":"context";null===t?null===i.use&&e.error("Tag could be omitted only for .use()"):null===i.use&&(n=this._encodeComposite(t,s,r,o))}return null!==i.explicit&&(n=this._encodeComposite(i.explicit,!1,"context",n)),n},bA.prototype._encodeChoice=function(t,e){const r=this._baseState,i=r.choice[t.type];return i||dA(!1,t.type+" not found in "+JSON.stringify(Object.keys(r.choice))),i._encode(t.value,e)},bA.prototype._encodePrimitive=function(t,e){const r=this._baseState;if(/str$/.test(t))return this._encodeStr(e,t);if("objid"===t&&r.args)return this._encodeObjid(e,r.reverseArgs[0],r.args[1]);if("objid"===t)return this._encodeObjid(e,null,null);if("gentime"===t||"utctime"===t)return this._encodeTime(e,t);if("null_"===t)return this._encodeNull();if("int"===t||"enum"===t)return this._encodeInt(e,r.args&&r.reverseArgs[0]);if("bool"===t)return this._encodeBool(e);if("objDesc"===t)return this._encodeStr(e,t);throw new Error("Unsupported tag: "+t)},bA.prototype._isNumstr=function(t){return/^[0-9 ]*$/.test(t)},bA.prototype._isPrintstr=function(t){return/^[A-Za-z0-9 '()+,-./:=?]*$/.test(t)}),hA}var wA,MA,_A,AA,EA,SA=!1;function kA(t){const e={};return Object.keys(t).forEach((function(r){(0|r)==r&&(r|=0);const i=t[r];e[i]=r})),e}function BA(){return SA||(SA=!0,(wA={}).tagClass=MA={0:"universal",1:"application",2:"context",3:"private"},_A=kA(MA),wA.tagClassByName=_A,AA={0:"end",1:"bool",2:"int",3:"bitstr",4:"octstr",5:"null_",6:"objid",7:"objDesc",8:"external",9:"real",10:"enum",11:"embed",12:"utf8str",13:"relativeOid",16:"seq",17:"set",18:"numstr",19:"printstr",20:"t61str",21:"videostr",22:"ia5str",23:"utctime",24:"gentime",25:"graphstr",26:"iso646str",27:"genstr",28:"unistr",29:"charstr",30:"bmpstr"},wA.tag=AA,EA=kA(AA),wA.tagByName=EA),wA}var RA,TA,xA,IA,CA=!1;function PA(t){this.enc="der",this.name=t.name,this.entity=t,this.tree=new DA,this.tree._init(t.body)}function DA(t){IA.call(this,"der",t)}function LA(t){return t<10?"0"+t:t}function OA(){RA={},TA=Ar(),xA=H_().Buffer,IA=yA(),BA(),RA=PA,PA.prototype.encode=function(t,e){return this.tree._encode(t,e).join()},TA(DA,IA),DA.prototype._encodeComposite=function(t,e,r,i){const n=function(t,e,r,i){let n;if("seqof"===t?t="seq":"setof"===t&&(t="set"),BA().tagByName.hasOwnProperty(t))n=BA().tagByName[t];else{if("number"!=typeof t||(0|t)!==t)return i.error("Unknown tag: "+t);n=t}return n>=31?i.error("Multi-octet tag encoding unsupported"):(e||(n|=32),n|=BA().tagClassByName[r||"universal"]<<6,n)}(t,e,r,this.reporter);if(i.length<128){const t=xA.alloc(2);return t[0]=n,t[1]=i.length,this._createEncoderBuffer([t,i])}let o=1;for(let t=i.length;t>=256;t>>=8)o++;const s=xA.alloc(2+o);s[0]=n,s[1]=128|o;for(let t=1+o,e=i.length;e>0;t--,e>>=8)s[t]=255&e;return this._createEncoderBuffer([s,i])},DA.prototype._encodeStr=function(t,e){if("bitstr"===e)return this._createEncoderBuffer([0|t.unused,t.data]);if("bmpstr"===e){const e=xA.alloc(2*t.length);for(let r=0;r=40)return this.reporter.error("Second objid identifier OOB");t.splice(0,2,40*t[0]+t[1])}let i=0;for(let e=0;e=128;r>>=7)i++}const n=xA.alloc(i);let o=n.length-1;for(let e=t.length-1;e>=0;e--){let r=t[e];for(n[o--]=127&r;(r>>=7)>0;)n[o--]=128|127&r}return this._createEncoderBuffer(n)},DA.prototype._encodeTime=function(t,e){let r;const i=new Date(t);return"gentime"===e?r=[LA(i.getUTCFullYear()),LA(i.getUTCMonth()+1),LA(i.getUTCDate()),LA(i.getUTCHours()),LA(i.getUTCMinutes()),LA(i.getUTCSeconds()),"Z"].join(""):"utctime"===e?r=[LA(i.getUTCFullYear()%100),LA(i.getUTCMonth()+1),LA(i.getUTCDate()),LA(i.getUTCHours()),LA(i.getUTCMinutes()),LA(i.getUTCSeconds()),"Z"].join(""):this.reporter.error("Encoding "+e+" time is not supported yet"),this._encodeStr(r,"octstr")},DA.prototype._encodeNull=function(){return this._createEncoderBuffer("")},DA.prototype._encodeInt=function(t,e){if("string"==typeof t){if(!e)return this.reporter.error("String int or enum given, but no values map");if(!e.hasOwnProperty(t))return this.reporter.error("Values map doesn't contain: "+JSON.stringify(t));t=e[t]}if("number"!=typeof t&&!xA.isBuffer(t)){const e=t.toArray();!t.sign&&128&e[0]&&e.unshift(0),t=xA.from(e)}if(xA.isBuffer(t)){let e=t.length;0===t.length&&e++;const r=xA.alloc(e);return t.copy(r),0===t.length&&(r[0]=0),this._createEncoderBuffer(r)}if(t<128)return this._createEncoderBuffer(t);if(t<256)return this._createEncoderBuffer([0,t]);let r=1;for(let e=t;e>=256;e>>=8)r++;const i=new Array(r);for(let e=i.length-1;e>=0;e--)i[e]=255&t,t>>=8;return 128&i[0]&&i.unshift(0),this._createEncoderBuffer(xA.from(i))},DA.prototype._encodeBool=function(t){return this._createEncoderBuffer(t?255:0)},DA.prototype._use=function(t,e){return"function"==typeof t&&(t=t(e)),t._getEncoder("der").tree},DA.prototype._skipDefault=function(t,e,r){const i=this._baseState;let n;if(null===i.default)return!1;const o=t.join();if(void 0===i.defaultBuffer&&(i.defaultBuffer=this._encodeValue(i.default,e,r).join()),o.length!==i.defaultBuffer.length)return!1;for(n=0;n>6],n=0==(32&r);if(31==(31&r)){let i=r;for(r=0;128==(128&i);){if(i=t.readUInt8(e),t.isError(i))return i;r<<=7,r|=127&i}}else r&=31;return{cls:i,primitive:n,tag:r,tagStr:BA().tag[r]}}function iE(t,e,r){let i=t.readUInt8(r);if(t.isError(i))return i;if(!e&&128===i)return null;if(0==(128&i))return i;const n=127&i;if(n>4)return t.error("length octect is too long");i=0;for(let e=0;e0&&r.ishrn(i),r}function JS(t,e,r){var i,n;do{for(i=NS.alloc(0);8*i.length=e)throw new Error("invalid sig")}function fk(){return ak||(ak=!0,ek={},rk=lr().Buffer,ik=Dg(),nk=C_().ec,ok=DS(),sk=jS(),ek=hk),ek}var lk,dk,ck,pk,mk,gk,vk,bk=!1;function yk(t){Ls().Writable.call(this);var e=vk[t];if(!e)throw new Error("Unknown message digest");this._hashType=e.hash,this._hash=ck(e.hash),this._tag=e.id,this._signType=e.sign}function wk(t){Ls().Writable.call(this);var e=vk[t];if(!e)throw new Error("Unknown message digest");this._hash=ck(e.hash),this._tag=e.id,this._signType=e.sign}function Mk(t){return new yk(t)}function _k(t){return new wk(t)}function Ak(){return bk||(bk=!0,lk={},dk=lr().Buffer,ck=uu(),Ls(),pk=Ar(),mk=tk(),gk=fk(),vk=ju(),Object.keys(vk).forEach((function(t){vk[t].id=dk.from(vk[t].id,"hex"),vk[t.toLowerCase()]=vk[t]})),pk(yk,Ls().Writable),yk.prototype._write=function(t,e,r){this._hash.update(t),r()},yk.prototype.update=function(t,e){return"string"==typeof t&&(t=dk.from(t,e)),this._hash.update(t),this},yk.prototype.sign=function(t,e){this.end();var r=this._hash.digest(),i=mk(r,t,this._hashType,this._signType,this._tag);return e?i.toString(e):i},pk(wk,Ls().Writable),wk.prototype._write=function(t,e,r){this._hash.update(t),r()},wk.prototype.update=function(t,e){return"string"==typeof t&&(t=dk.from(t,e)),this._hash.update(t),this},wk.prototype.verify=function(t,e,r){"string"==typeof e&&(e=dk.from(e,r)),this.end();var i=this._hash.digest();return gk(e,i,t,this._signType,this._tag)},lk={Sign:Mk,Verify:_k,createSign:Mk,createVerify:_k}),lk}var Ek,Sk=!1;function kk(){Ek=function(){var t={exports:this};return function(t,e){function r(t,e){if(!t)throw new Error(e||"Assertion failed")}function i(t,e){t.super_=e;var r=function(){};r.prototype=e.prototype,t.prototype=new r,t.prototype.constructor=t}function n(t,e,r){if(n.isBN(t))return t;this.negative=0,this.words=null,this.length=0,this.red=null,null!==t&&("le"!==e&&"be"!==e||(r=e,e=10),this._init(t||0,e||10,r||"be"))}var o;"object"==typeof t?t.exports=n:e.BN=n,n.BN=n,n.wordSize=26;try{o="undefined"!=typeof window&&void 0!==window.Buffer?window.Buffer:ir().Buffer}catch(t){}function s(t,e){var r=t.charCodeAt(e);return r>=65&&r<=70?r-55:r>=97&&r<=102?r-87:r-48&15}function a(t,e,r){var i=s(t,r);return r-1>=e&&(i|=s(t,r-1)<<4),i}function h(t,e,r,i){for(var n=0,o=Math.min(t.length,r),s=e;s=49?a-49+10:a>=17?a-17+10:a}return n}n.isBN=function(t){return t instanceof n||null!==t&&"object"==typeof t&&t.constructor.wordSize===n.wordSize&&Array.isArray(t.words)},n.max=function(t,e){return t.cmp(e)>0?t:e},n.min=function(t,e){return t.cmp(e)<0?t:e},n.prototype._init=function(t,e,i){if("number"==typeof t)return this._initNumber(t,e,i);if("object"==typeof t)return this._initArray(t,e,i);"hex"===e&&(e=16),r(e===(0|e)&&e>=2&&e<=36);var n=0;"-"===(t=t.toString().replace(/\s+/g,""))[0]&&(n++,this.negative=1),n=0;n-=3)s=t[n]|t[n-1]<<8|t[n-2]<<16,this.words[o]|=s<>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===i)for(n=0,o=0;n>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this.strip()},n.prototype._parseHex=function(t,e,r){this.length=Math.ceil((t.length-e)/6),this.words=new Array(this.length);for(var i=0;i=e;i-=2)n=a(t,e,i)<=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;else for(i=(t.length-e)%2==0?e+1:e;i=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;this.strip()},n.prototype._parseBase=function(t,e,r){this.words=[0],this.length=1;for(var i=0,n=1;n<=67108863;n*=e)i++;i--,n=n/e|0;for(var o=t.length-r,s=o%i,a=Math.min(o,o-s)+r,u=0,f=r;f1&&0===this.words[this.length-1];)this.length--;return this._normSign()},n.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},n.prototype.inspect=function(){return(this.red?""};var u=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],f=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],l=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];function d(t,e,r){r.negative=e.negative^t.negative;var i=t.length+e.length|0;r.length=i,i=i-1|0;var n=0|t.words[0],o=0|e.words[0],s=n*o,a=67108863&s,h=s/67108864|0;r.words[0]=a;for(var u=1;u>>26,l=67108863&h,d=Math.min(u,e.length-1),c=Math.max(0,u-t.length+1);c<=d;c++){var p=u-c|0;f+=(s=(n=0|t.words[p])*(o=0|e.words[c])+l)/67108864|0,l=67108863&s}r.words[u]=0|l,h=0|f}return 0!==h?r.words[u]=0|h:r.length--,r.strip()}n.prototype.toString=function(t,e){var i;if(e=0|e||1,16===(t=t||10)||"hex"===t){i="";for(var n=0,o=0,s=0;s>>24-n&16777215)||s!==this.length-1?u[6-h.length]+h+i:h+i,(n+=2)>=26&&(n-=26,s--)}for(0!==o&&(i=o.toString(16)+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}if(t===(0|t)&&t>=2&&t<=36){var d=f[t],c=l[t];i="";var p=this.clone();for(p.negative=0;!p.isZero();){var m=p.modn(c).toString(t);i=(p=p.idivn(c)).isZero()?m+i:u[d-m.length]+m+i}for(this.isZero()&&(i="0"+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}r(!1,"Base should be between 2 and 36")},n.prototype.toNumber=function(){var t=this.words[0];return 2===this.length?t+=67108864*this.words[1]:3===this.length&&1===this.words[2]?t+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-t:t},n.prototype.toJSON=function(){return this.toString(16)},n.prototype.toBuffer=function(t,e){return r(void 0!==o),this.toArrayLike(o,t,e)},n.prototype.toArray=function(t,e){return this.toArrayLike(Array,t,e)},n.prototype.toArrayLike=function(t,e,i){var n=this.byteLength(),o=i||Math.max(1,n);r(n<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0"),this.strip();var s,a,h="le"===e,u=new t(o),f=this.clone();if(h){for(a=0;!f.isZero();a++)s=f.andln(255),f.iushrn(8),u[a]=s;for(;a=4096&&(r+=13,e>>>=13),e>=64&&(r+=7,e>>>=7),e>=8&&(r+=4,e>>>=4),e>=2&&(r+=2,e>>>=2),r+e},n.prototype._zeroBits=function(t){if(0===t)return 26;var e=t,r=0;return 0==(8191&e)&&(r+=13,e>>>=13),0==(127&e)&&(r+=7,e>>>=7),0==(15&e)&&(r+=4,e>>>=4),0==(3&e)&&(r+=2,e>>>=2),0==(1&e)&&r++,r},n.prototype.bitLength=function(){var t=this.words[this.length-1],e=this._countBits(t);return 26*(this.length-1)+e},n.prototype.zeroBits=function(){if(this.isZero())return 0;for(var t=0,e=0;et.length?this.clone().ior(t):t.clone().ior(this)},n.prototype.uor=function(t){return this.length>t.length?this.clone().iuor(t):t.clone().iuor(this)},n.prototype.iuand=function(t){var e;e=this.length>t.length?t:this;for(var r=0;rt.length?this.clone().iand(t):t.clone().iand(this)},n.prototype.uand=function(t){return this.length>t.length?this.clone().iuand(t):t.clone().iuand(this)},n.prototype.iuxor=function(t){var e,r;this.length>t.length?(e=this,r=t):(e=t,r=this);for(var i=0;it.length?this.clone().ixor(t):t.clone().ixor(this)},n.prototype.uxor=function(t){return this.length>t.length?this.clone().iuxor(t):t.clone().iuxor(this)},n.prototype.inotn=function(t){r("number"==typeof t&&t>=0);var e=0|Math.ceil(t/26),i=t%26;this._expand(e),i>0&&e--;for(var n=0;n0&&(this.words[n]=~this.words[n]&67108863>>26-i),this.strip()},n.prototype.notn=function(t){return this.clone().inotn(t)},n.prototype.setn=function(t,e){r("number"==typeof t&&t>=0);var i=t/26|0,n=t%26;return this._expand(i+1),this.words[i]=e?this.words[i]|1<t.length?(r=this,i=t):(r=t,i=this);for(var n=0,o=0;o>>26;for(;0!==n&&o>>26;if(this.length=r.length,0!==n)this.words[this.length]=n,this.length++;else if(r!==this)for(;ot.length?this.clone().iadd(t):t.clone().iadd(this)},n.prototype.isub=function(t){if(0!==t.negative){t.negative=0;var e=this.iadd(t);return t.negative=1,e._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(t),this.negative=1,this._normSign();var r,i,n=this.cmp(t);if(0===n)return this.negative=0,this.length=1,this.words[0]=0,this;n>0?(r=this,i=t):(r=t,i=this);for(var o=0,s=0;s>26,this.words[s]=67108863&e;for(;0!==o&&s>26,this.words[s]=67108863&e;if(0===o&&s>>13,c=0|s[1],p=8191&c,m=c>>>13,g=0|s[2],v=8191&g,b=g>>>13,y=0|s[3],w=8191&y,M=y>>>13,_=0|s[4],A=8191&_,E=_>>>13,S=0|s[5],k=8191&S,B=S>>>13,R=0|s[6],T=8191&R,x=R>>>13,I=0|s[7],C=8191&I,P=I>>>13,D=0|s[8],L=8191&D,O=D>>>13,j=0|s[9],U=8191&j,N=j>>>13,z=0|a[0],q=8191&z,F=z>>>13,Z=0|a[1],H=8191&Z,K=Z>>>13,W=0|a[2],G=8191&W,Y=W>>>13,V=0|a[3],$=8191&V,X=V>>>13,J=0|a[4],Q=8191&J,tt=J>>>13,et=0|a[5],rt=8191&et,it=et>>>13,nt=0|a[6],ot=8191&nt,st=nt>>>13,at=0|a[7],ht=8191&at,ut=at>>>13,ft=0|a[8],lt=8191&ft,dt=ft>>>13,ct=0|a[9],pt=8191&ct,mt=ct>>>13;r.negative=t.negative^e.negative,r.length=19;var gt=(u+(i=Math.imul(l,q))|0)+((8191&(n=(n=Math.imul(l,F))+Math.imul(d,q)|0))<<13)|0;u=((o=Math.imul(d,F))+(n>>>13)|0)+(gt>>>26)|0,gt&=67108863,i=Math.imul(p,q),n=(n=Math.imul(p,F))+Math.imul(m,q)|0,o=Math.imul(m,F);var vt=(u+(i=i+Math.imul(l,H)|0)|0)+((8191&(n=(n=n+Math.imul(l,K)|0)+Math.imul(d,H)|0))<<13)|0;u=((o=o+Math.imul(d,K)|0)+(n>>>13)|0)+(vt>>>26)|0,vt&=67108863,i=Math.imul(v,q),n=(n=Math.imul(v,F))+Math.imul(b,q)|0,o=Math.imul(b,F),i=i+Math.imul(p,H)|0,n=(n=n+Math.imul(p,K)|0)+Math.imul(m,H)|0,o=o+Math.imul(m,K)|0;var bt=(u+(i=i+Math.imul(l,G)|0)|0)+((8191&(n=(n=n+Math.imul(l,Y)|0)+Math.imul(d,G)|0))<<13)|0;u=((o=o+Math.imul(d,Y)|0)+(n>>>13)|0)+(bt>>>26)|0,bt&=67108863,i=Math.imul(w,q),n=(n=Math.imul(w,F))+Math.imul(M,q)|0,o=Math.imul(M,F),i=i+Math.imul(v,H)|0,n=(n=n+Math.imul(v,K)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,K)|0,i=i+Math.imul(p,G)|0,n=(n=n+Math.imul(p,Y)|0)+Math.imul(m,G)|0,o=o+Math.imul(m,Y)|0;var yt=(u+(i=i+Math.imul(l,$)|0)|0)+((8191&(n=(n=n+Math.imul(l,X)|0)+Math.imul(d,$)|0))<<13)|0;u=((o=o+Math.imul(d,X)|0)+(n>>>13)|0)+(yt>>>26)|0,yt&=67108863,i=Math.imul(A,q),n=(n=Math.imul(A,F))+Math.imul(E,q)|0,o=Math.imul(E,F),i=i+Math.imul(w,H)|0,n=(n=n+Math.imul(w,K)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,K)|0,i=i+Math.imul(v,G)|0,n=(n=n+Math.imul(v,Y)|0)+Math.imul(b,G)|0,o=o+Math.imul(b,Y)|0,i=i+Math.imul(p,$)|0,n=(n=n+Math.imul(p,X)|0)+Math.imul(m,$)|0,o=o+Math.imul(m,X)|0;var wt=(u+(i=i+Math.imul(l,Q)|0)|0)+((8191&(n=(n=n+Math.imul(l,tt)|0)+Math.imul(d,Q)|0))<<13)|0;u=((o=o+Math.imul(d,tt)|0)+(n>>>13)|0)+(wt>>>26)|0,wt&=67108863,i=Math.imul(k,q),n=(n=Math.imul(k,F))+Math.imul(B,q)|0,o=Math.imul(B,F),i=i+Math.imul(A,H)|0,n=(n=n+Math.imul(A,K)|0)+Math.imul(E,H)|0,o=o+Math.imul(E,K)|0,i=i+Math.imul(w,G)|0,n=(n=n+Math.imul(w,Y)|0)+Math.imul(M,G)|0,o=o+Math.imul(M,Y)|0,i=i+Math.imul(v,$)|0,n=(n=n+Math.imul(v,X)|0)+Math.imul(b,$)|0,o=o+Math.imul(b,X)|0,i=i+Math.imul(p,Q)|0,n=(n=n+Math.imul(p,tt)|0)+Math.imul(m,Q)|0,o=o+Math.imul(m,tt)|0;var Mt=(u+(i=i+Math.imul(l,rt)|0)|0)+((8191&(n=(n=n+Math.imul(l,it)|0)+Math.imul(d,rt)|0))<<13)|0;u=((o=o+Math.imul(d,it)|0)+(n>>>13)|0)+(Mt>>>26)|0,Mt&=67108863,i=Math.imul(T,q),n=(n=Math.imul(T,F))+Math.imul(x,q)|0,o=Math.imul(x,F),i=i+Math.imul(k,H)|0,n=(n=n+Math.imul(k,K)|0)+Math.imul(B,H)|0,o=o+Math.imul(B,K)|0,i=i+Math.imul(A,G)|0,n=(n=n+Math.imul(A,Y)|0)+Math.imul(E,G)|0,o=o+Math.imul(E,Y)|0,i=i+Math.imul(w,$)|0,n=(n=n+Math.imul(w,X)|0)+Math.imul(M,$)|0,o=o+Math.imul(M,X)|0,i=i+Math.imul(v,Q)|0,n=(n=n+Math.imul(v,tt)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,tt)|0,i=i+Math.imul(p,rt)|0,n=(n=n+Math.imul(p,it)|0)+Math.imul(m,rt)|0,o=o+Math.imul(m,it)|0;var _t=(u+(i=i+Math.imul(l,ot)|0)|0)+((8191&(n=(n=n+Math.imul(l,st)|0)+Math.imul(d,ot)|0))<<13)|0;u=((o=o+Math.imul(d,st)|0)+(n>>>13)|0)+(_t>>>26)|0,_t&=67108863,i=Math.imul(C,q),n=(n=Math.imul(C,F))+Math.imul(P,q)|0,o=Math.imul(P,F),i=i+Math.imul(T,H)|0,n=(n=n+Math.imul(T,K)|0)+Math.imul(x,H)|0,o=o+Math.imul(x,K)|0,i=i+Math.imul(k,G)|0,n=(n=n+Math.imul(k,Y)|0)+Math.imul(B,G)|0,o=o+Math.imul(B,Y)|0,i=i+Math.imul(A,$)|0,n=(n=n+Math.imul(A,X)|0)+Math.imul(E,$)|0,o=o+Math.imul(E,X)|0,i=i+Math.imul(w,Q)|0,n=(n=n+Math.imul(w,tt)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,tt)|0,i=i+Math.imul(v,rt)|0,n=(n=n+Math.imul(v,it)|0)+Math.imul(b,rt)|0,o=o+Math.imul(b,it)|0,i=i+Math.imul(p,ot)|0,n=(n=n+Math.imul(p,st)|0)+Math.imul(m,ot)|0,o=o+Math.imul(m,st)|0;var At=(u+(i=i+Math.imul(l,ht)|0)|0)+((8191&(n=(n=n+Math.imul(l,ut)|0)+Math.imul(d,ht)|0))<<13)|0;u=((o=o+Math.imul(d,ut)|0)+(n>>>13)|0)+(At>>>26)|0,At&=67108863,i=Math.imul(L,q),n=(n=Math.imul(L,F))+Math.imul(O,q)|0,o=Math.imul(O,F),i=i+Math.imul(C,H)|0,n=(n=n+Math.imul(C,K)|0)+Math.imul(P,H)|0,o=o+Math.imul(P,K)|0,i=i+Math.imul(T,G)|0,n=(n=n+Math.imul(T,Y)|0)+Math.imul(x,G)|0,o=o+Math.imul(x,Y)|0,i=i+Math.imul(k,$)|0,n=(n=n+Math.imul(k,X)|0)+Math.imul(B,$)|0,o=o+Math.imul(B,X)|0,i=i+Math.imul(A,Q)|0,n=(n=n+Math.imul(A,tt)|0)+Math.imul(E,Q)|0,o=o+Math.imul(E,tt)|0,i=i+Math.imul(w,rt)|0,n=(n=n+Math.imul(w,it)|0)+Math.imul(M,rt)|0,o=o+Math.imul(M,it)|0,i=i+Math.imul(v,ot)|0,n=(n=n+Math.imul(v,st)|0)+Math.imul(b,ot)|0,o=o+Math.imul(b,st)|0,i=i+Math.imul(p,ht)|0,n=(n=n+Math.imul(p,ut)|0)+Math.imul(m,ht)|0,o=o+Math.imul(m,ut)|0;var Et=(u+(i=i+Math.imul(l,lt)|0)|0)+((8191&(n=(n=n+Math.imul(l,dt)|0)+Math.imul(d,lt)|0))<<13)|0;u=((o=o+Math.imul(d,dt)|0)+(n>>>13)|0)+(Et>>>26)|0,Et&=67108863,i=Math.imul(U,q),n=(n=Math.imul(U,F))+Math.imul(N,q)|0,o=Math.imul(N,F),i=i+Math.imul(L,H)|0,n=(n=n+Math.imul(L,K)|0)+Math.imul(O,H)|0,o=o+Math.imul(O,K)|0,i=i+Math.imul(C,G)|0,n=(n=n+Math.imul(C,Y)|0)+Math.imul(P,G)|0,o=o+Math.imul(P,Y)|0,i=i+Math.imul(T,$)|0,n=(n=n+Math.imul(T,X)|0)+Math.imul(x,$)|0,o=o+Math.imul(x,X)|0,i=i+Math.imul(k,Q)|0,n=(n=n+Math.imul(k,tt)|0)+Math.imul(B,Q)|0,o=o+Math.imul(B,tt)|0,i=i+Math.imul(A,rt)|0,n=(n=n+Math.imul(A,it)|0)+Math.imul(E,rt)|0,o=o+Math.imul(E,it)|0,i=i+Math.imul(w,ot)|0,n=(n=n+Math.imul(w,st)|0)+Math.imul(M,ot)|0,o=o+Math.imul(M,st)|0,i=i+Math.imul(v,ht)|0,n=(n=n+Math.imul(v,ut)|0)+Math.imul(b,ht)|0,o=o+Math.imul(b,ut)|0,i=i+Math.imul(p,lt)|0,n=(n=n+Math.imul(p,dt)|0)+Math.imul(m,lt)|0,o=o+Math.imul(m,dt)|0;var St=(u+(i=i+Math.imul(l,pt)|0)|0)+((8191&(n=(n=n+Math.imul(l,mt)|0)+Math.imul(d,pt)|0))<<13)|0;u=((o=o+Math.imul(d,mt)|0)+(n>>>13)|0)+(St>>>26)|0,St&=67108863,i=Math.imul(U,H),n=(n=Math.imul(U,K))+Math.imul(N,H)|0,o=Math.imul(N,K),i=i+Math.imul(L,G)|0,n=(n=n+Math.imul(L,Y)|0)+Math.imul(O,G)|0,o=o+Math.imul(O,Y)|0,i=i+Math.imul(C,$)|0,n=(n=n+Math.imul(C,X)|0)+Math.imul(P,$)|0,o=o+Math.imul(P,X)|0,i=i+Math.imul(T,Q)|0,n=(n=n+Math.imul(T,tt)|0)+Math.imul(x,Q)|0,o=o+Math.imul(x,tt)|0,i=i+Math.imul(k,rt)|0,n=(n=n+Math.imul(k,it)|0)+Math.imul(B,rt)|0,o=o+Math.imul(B,it)|0,i=i+Math.imul(A,ot)|0,n=(n=n+Math.imul(A,st)|0)+Math.imul(E,ot)|0,o=o+Math.imul(E,st)|0,i=i+Math.imul(w,ht)|0,n=(n=n+Math.imul(w,ut)|0)+Math.imul(M,ht)|0,o=o+Math.imul(M,ut)|0,i=i+Math.imul(v,lt)|0,n=(n=n+Math.imul(v,dt)|0)+Math.imul(b,lt)|0,o=o+Math.imul(b,dt)|0;var kt=(u+(i=i+Math.imul(p,pt)|0)|0)+((8191&(n=(n=n+Math.imul(p,mt)|0)+Math.imul(m,pt)|0))<<13)|0;u=((o=o+Math.imul(m,mt)|0)+(n>>>13)|0)+(kt>>>26)|0,kt&=67108863,i=Math.imul(U,G),n=(n=Math.imul(U,Y))+Math.imul(N,G)|0,o=Math.imul(N,Y),i=i+Math.imul(L,$)|0,n=(n=n+Math.imul(L,X)|0)+Math.imul(O,$)|0,o=o+Math.imul(O,X)|0,i=i+Math.imul(C,Q)|0,n=(n=n+Math.imul(C,tt)|0)+Math.imul(P,Q)|0,o=o+Math.imul(P,tt)|0,i=i+Math.imul(T,rt)|0,n=(n=n+Math.imul(T,it)|0)+Math.imul(x,rt)|0,o=o+Math.imul(x,it)|0,i=i+Math.imul(k,ot)|0,n=(n=n+Math.imul(k,st)|0)+Math.imul(B,ot)|0,o=o+Math.imul(B,st)|0,i=i+Math.imul(A,ht)|0,n=(n=n+Math.imul(A,ut)|0)+Math.imul(E,ht)|0,o=o+Math.imul(E,ut)|0,i=i+Math.imul(w,lt)|0,n=(n=n+Math.imul(w,dt)|0)+Math.imul(M,lt)|0,o=o+Math.imul(M,dt)|0;var Bt=(u+(i=i+Math.imul(v,pt)|0)|0)+((8191&(n=(n=n+Math.imul(v,mt)|0)+Math.imul(b,pt)|0))<<13)|0;u=((o=o+Math.imul(b,mt)|0)+(n>>>13)|0)+(Bt>>>26)|0,Bt&=67108863,i=Math.imul(U,$),n=(n=Math.imul(U,X))+Math.imul(N,$)|0,o=Math.imul(N,X),i=i+Math.imul(L,Q)|0,n=(n=n+Math.imul(L,tt)|0)+Math.imul(O,Q)|0,o=o+Math.imul(O,tt)|0,i=i+Math.imul(C,rt)|0,n=(n=n+Math.imul(C,it)|0)+Math.imul(P,rt)|0,o=o+Math.imul(P,it)|0,i=i+Math.imul(T,ot)|0,n=(n=n+Math.imul(T,st)|0)+Math.imul(x,ot)|0,o=o+Math.imul(x,st)|0,i=i+Math.imul(k,ht)|0,n=(n=n+Math.imul(k,ut)|0)+Math.imul(B,ht)|0,o=o+Math.imul(B,ut)|0,i=i+Math.imul(A,lt)|0,n=(n=n+Math.imul(A,dt)|0)+Math.imul(E,lt)|0,o=o+Math.imul(E,dt)|0;var Rt=(u+(i=i+Math.imul(w,pt)|0)|0)+((8191&(n=(n=n+Math.imul(w,mt)|0)+Math.imul(M,pt)|0))<<13)|0;u=((o=o+Math.imul(M,mt)|0)+(n>>>13)|0)+(Rt>>>26)|0,Rt&=67108863,i=Math.imul(U,Q),n=(n=Math.imul(U,tt))+Math.imul(N,Q)|0,o=Math.imul(N,tt),i=i+Math.imul(L,rt)|0,n=(n=n+Math.imul(L,it)|0)+Math.imul(O,rt)|0,o=o+Math.imul(O,it)|0,i=i+Math.imul(C,ot)|0,n=(n=n+Math.imul(C,st)|0)+Math.imul(P,ot)|0,o=o+Math.imul(P,st)|0,i=i+Math.imul(T,ht)|0,n=(n=n+Math.imul(T,ut)|0)+Math.imul(x,ht)|0,o=o+Math.imul(x,ut)|0,i=i+Math.imul(k,lt)|0,n=(n=n+Math.imul(k,dt)|0)+Math.imul(B,lt)|0,o=o+Math.imul(B,dt)|0;var Tt=(u+(i=i+Math.imul(A,pt)|0)|0)+((8191&(n=(n=n+Math.imul(A,mt)|0)+Math.imul(E,pt)|0))<<13)|0;u=((o=o+Math.imul(E,mt)|0)+(n>>>13)|0)+(Tt>>>26)|0,Tt&=67108863,i=Math.imul(U,rt),n=(n=Math.imul(U,it))+Math.imul(N,rt)|0,o=Math.imul(N,it),i=i+Math.imul(L,ot)|0,n=(n=n+Math.imul(L,st)|0)+Math.imul(O,ot)|0,o=o+Math.imul(O,st)|0,i=i+Math.imul(C,ht)|0,n=(n=n+Math.imul(C,ut)|0)+Math.imul(P,ht)|0,o=o+Math.imul(P,ut)|0,i=i+Math.imul(T,lt)|0,n=(n=n+Math.imul(T,dt)|0)+Math.imul(x,lt)|0,o=o+Math.imul(x,dt)|0;var xt=(u+(i=i+Math.imul(k,pt)|0)|0)+((8191&(n=(n=n+Math.imul(k,mt)|0)+Math.imul(B,pt)|0))<<13)|0;u=((o=o+Math.imul(B,mt)|0)+(n>>>13)|0)+(xt>>>26)|0,xt&=67108863,i=Math.imul(U,ot),n=(n=Math.imul(U,st))+Math.imul(N,ot)|0,o=Math.imul(N,st),i=i+Math.imul(L,ht)|0,n=(n=n+Math.imul(L,ut)|0)+Math.imul(O,ht)|0,o=o+Math.imul(O,ut)|0,i=i+Math.imul(C,lt)|0,n=(n=n+Math.imul(C,dt)|0)+Math.imul(P,lt)|0,o=o+Math.imul(P,dt)|0;var It=(u+(i=i+Math.imul(T,pt)|0)|0)+((8191&(n=(n=n+Math.imul(T,mt)|0)+Math.imul(x,pt)|0))<<13)|0;u=((o=o+Math.imul(x,mt)|0)+(n>>>13)|0)+(It>>>26)|0,It&=67108863,i=Math.imul(U,ht),n=(n=Math.imul(U,ut))+Math.imul(N,ht)|0,o=Math.imul(N,ut),i=i+Math.imul(L,lt)|0,n=(n=n+Math.imul(L,dt)|0)+Math.imul(O,lt)|0,o=o+Math.imul(O,dt)|0;var Ct=(u+(i=i+Math.imul(C,pt)|0)|0)+((8191&(n=(n=n+Math.imul(C,mt)|0)+Math.imul(P,pt)|0))<<13)|0;u=((o=o+Math.imul(P,mt)|0)+(n>>>13)|0)+(Ct>>>26)|0,Ct&=67108863,i=Math.imul(U,lt),n=(n=Math.imul(U,dt))+Math.imul(N,lt)|0,o=Math.imul(N,dt);var Pt=(u+(i=i+Math.imul(L,pt)|0)|0)+((8191&(n=(n=n+Math.imul(L,mt)|0)+Math.imul(O,pt)|0))<<13)|0;u=((o=o+Math.imul(O,mt)|0)+(n>>>13)|0)+(Pt>>>26)|0,Pt&=67108863;var Dt=(u+(i=Math.imul(U,pt))|0)+((8191&(n=(n=Math.imul(U,mt))+Math.imul(N,pt)|0))<<13)|0;return u=((o=Math.imul(N,mt))+(n>>>13)|0)+(Dt>>>26)|0,Dt&=67108863,h[0]=gt,h[1]=vt,h[2]=bt,h[3]=yt,h[4]=wt,h[5]=Mt,h[6]=_t,h[7]=At,h[8]=Et,h[9]=St,h[10]=kt,h[11]=Bt,h[12]=Rt,h[13]=Tt,h[14]=xt,h[15]=It,h[16]=Ct,h[17]=Pt,h[18]=Dt,0!==u&&(h[19]=u,r.length++),r};function p(t,e,r){return(new m).mulp(t,e,r)}function m(t,e){this.x=t,this.y=e}Math.imul||(c=d),n.prototype.mulTo=function(t,e){var r,i=this.length+t.length;return r=10===this.length&&10===t.length?c(this,t,e):i<63?d(this,t,e):i<1024?function(t,e,r){r.negative=e.negative^t.negative,r.length=t.length+e.length;for(var i=0,n=0,o=0;o>>26)|0)>>>26,s&=67108863}r.words[o]=a,i=s,s=n}return 0!==i?r.words[o]=i:r.length--,r.strip()}(this,t,e):p(this,t,e),r},m.prototype.makeRBT=function(t){for(var e=new Array(t),r=n.prototype._countBits(t)-1,i=0;i>=1;return i},m.prototype.permute=function(t,e,r,i,n,o){for(var s=0;s>>=1)n++;return 1<>>=13,i[2*s+1]=8191&o,o>>>=13;for(s=2*e;s>=26,e+=n/67108864|0,e+=o>>>26,this.words[i]=67108863&o}return 0!==e&&(this.words[i]=e,this.length++),this},n.prototype.muln=function(t){return this.clone().imuln(t)},n.prototype.sqr=function(){return this.mul(this)},n.prototype.isqr=function(){return this.imul(this.clone())},n.prototype.pow=function(t){var e=function(t){for(var e=new Array(t.bitLength()),r=0;r>>n}return e}(t);if(0===e.length)return new n(1);for(var r=this,i=0;i=0);var e,i=t%26,n=(t-i)/26,o=67108863>>>26-i<<26-i;if(0!==i){var s=0;for(e=0;e>>26-i}s&&(this.words[e]=s,this.length++)}if(0!==n){for(e=this.length-1;e>=0;e--)this.words[e+n]=this.words[e];for(e=0;e=0),n=e?(e-e%26)/26:0;var o=t%26,s=Math.min((t-o)/26,this.length),a=67108863^67108863>>>o<s)for(this.length-=s,u=0;u=0&&(0!==f||u>=n);u--){var l=0|this.words[u];this.words[u]=f<<26-o|l>>>o,f=l&a}return h&&0!==f&&(h.words[h.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},n.prototype.ishrn=function(t,e,i){return r(0===this.negative),this.iushrn(t,e,i)},n.prototype.shln=function(t){return this.clone().ishln(t)},n.prototype.ushln=function(t){return this.clone().iushln(t)},n.prototype.shrn=function(t){return this.clone().ishrn(t)},n.prototype.ushrn=function(t){return this.clone().iushrn(t)},n.prototype.testn=function(t){r("number"==typeof t&&t>=0);var e=t%26,i=(t-e)/26,n=1<=0);var e=t%26,i=(t-e)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=i)return this;if(0!==e&&i++,this.length=Math.min(i,this.length),0!==e){var n=67108863^67108863>>>e<=67108864;e++)this.words[e]-=67108864,e===this.length-1?this.words[e+1]=1:this.words[e+1]++;return this.length=Math.max(this.length,e+1),this},n.prototype.isubn=function(t){if(r("number"==typeof t),r(t<67108864),t<0)return this.iaddn(-t);if(0!==this.negative)return this.negative=0,this.iaddn(t),this.negative=1,this;if(this.words[0]-=t,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var e=0;e>26)-(h/67108864|0),this.words[n+i]=67108863&o}for(;n>26,this.words[n+i]=67108863&o;if(0===a)return this.strip();for(r(-1===a),a=0,n=0;n>26,this.words[n]=67108863&o;return this.negative=1,this.strip()},n.prototype._wordDiv=function(t,e){var r=(this.length,t.length),i=this.clone(),o=t,s=0|o.words[o.length-1];0!==(r=26-this._countBits(s))&&(o=o.ushln(r),i.iushln(r),s=0|o.words[o.length-1]);var a,h=i.length-o.length;if("mod"!==e){(a=new n(null)).length=h+1,a.words=new Array(a.length);for(var u=0;u=0;l--){var d=67108864*(0|i.words[o.length+l])+(0|i.words[o.length+l-1]);for(d=Math.min(d/s|0,67108863),i._ishlnsubmul(o,d,l);0!==i.negative;)d--,i.negative=0,i._ishlnsubmul(o,1,l),i.isZero()||(i.negative^=1);a&&(a.words[l]=d)}return a&&a.strip(),i.strip(),"div"!==e&&0!==r&&i.iushrn(r),{div:a||null,mod:i}},n.prototype.divmod=function(t,e,i){return r(!t.isZero()),this.isZero()?{div:new n(0),mod:new n(0)}:0!==this.negative&&0===t.negative?(a=this.neg().divmod(t,e),"mod"!==e&&(o=a.div.neg()),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.iadd(t)),{div:o,mod:s}):0===this.negative&&0!==t.negative?(a=this.divmod(t.neg(),e),"mod"!==e&&(o=a.div.neg()),{div:o,mod:a.mod}):0!=(this.negative&t.negative)?(a=this.neg().divmod(t.neg(),e),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.isub(t)),{div:a.div,mod:s}):t.length>this.length||this.cmp(t)<0?{div:new n(0),mod:this}:1===t.length?"div"===e?{div:this.divn(t.words[0]),mod:null}:"mod"===e?{div:null,mod:new n(this.modn(t.words[0]))}:{div:this.divn(t.words[0]),mod:new n(this.modn(t.words[0]))}:this._wordDiv(t,e);var o,s,a},n.prototype.div=function(t){return this.divmod(t,"div",!1).div},n.prototype.mod=function(t){return this.divmod(t,"mod",!1).mod},n.prototype.umod=function(t){return this.divmod(t,"mod",!0).mod},n.prototype.divRound=function(t){var e=this.divmod(t);if(e.mod.isZero())return e.div;var r=0!==e.div.negative?e.mod.isub(t):e.mod,i=t.ushrn(1),n=t.andln(1),o=r.cmp(i);return o<0||1===n&&0===o?e.div:0!==e.div.negative?e.div.isubn(1):e.div.iaddn(1)},n.prototype.modn=function(t){r(t<=67108863);for(var e=(1<<26)%t,i=0,n=this.length-1;n>=0;n--)i=(e*i+(0|this.words[n]))%t;return i},n.prototype.idivn=function(t){r(t<=67108863);for(var e=0,i=this.length-1;i>=0;i--){var n=(0|this.words[i])+67108864*e;this.words[i]=n/t|0,e=n%t}return this.strip()},n.prototype.divn=function(t){return this.clone().idivn(t)},n.prototype.egcd=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o=new n(1),s=new n(0),a=new n(0),h=new n(1),u=0;e.isEven()&&i.isEven();)e.iushrn(1),i.iushrn(1),++u;for(var f=i.clone(),l=e.clone();!e.isZero();){for(var d=0,c=1;0==(e.words[0]&c)&&d<26;++d,c<<=1);if(d>0)for(e.iushrn(d);d-- >0;)(o.isOdd()||s.isOdd())&&(o.iadd(f),s.isub(l)),o.iushrn(1),s.iushrn(1);for(var p=0,m=1;0==(i.words[0]&m)&&p<26;++p,m<<=1);if(p>0)for(i.iushrn(p);p-- >0;)(a.isOdd()||h.isOdd())&&(a.iadd(f),h.isub(l)),a.iushrn(1),h.iushrn(1);e.cmp(i)>=0?(e.isub(i),o.isub(a),s.isub(h)):(i.isub(e),a.isub(o),h.isub(s))}return{a:a,b:h,gcd:i.iushln(u)}},n.prototype._invmp=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o,s=new n(1),a=new n(0),h=i.clone();e.cmpn(1)>0&&i.cmpn(1)>0;){for(var u=0,f=1;0==(e.words[0]&f)&&u<26;++u,f<<=1);if(u>0)for(e.iushrn(u);u-- >0;)s.isOdd()&&s.iadd(h),s.iushrn(1);for(var l=0,d=1;0==(i.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(i.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(h),a.iushrn(1);e.cmp(i)>=0?(e.isub(i),s.isub(a)):(i.isub(e),a.isub(s))}return(o=0===e.cmpn(1)?s:a).cmpn(0)<0&&o.iadd(t),o},n.prototype.gcd=function(t){if(this.isZero())return t.abs();if(t.isZero())return this.abs();var e=this.clone(),r=t.clone();e.negative=0,r.negative=0;for(var i=0;e.isEven()&&r.isEven();i++)e.iushrn(1),r.iushrn(1);for(;;){for(;e.isEven();)e.iushrn(1);for(;r.isEven();)r.iushrn(1);var n=e.cmp(r);if(n<0){var o=e;e=r,r=o}else if(0===n||0===r.cmpn(1))break;e.isub(r)}return r.iushln(i)},n.prototype.invm=function(t){return this.egcd(t).a.umod(t)},n.prototype.isEven=function(){return 0==(1&this.words[0])},n.prototype.isOdd=function(){return 1==(1&this.words[0])},n.prototype.andln=function(t){return this.words[0]&t},n.prototype.bincn=function(t){r("number"==typeof t);var e=t%26,i=(t-e)/26,n=1<>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},n.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},n.prototype.cmpn=function(t){var e,i=t<0;if(0!==this.negative&&!i)return-1;if(0===this.negative&&i)return 1;if(this.strip(),this.length>1)e=1;else{i&&(t=-t),r(t<=67108863,"Number is too big");var n=0|this.words[0];e=n===t?0:nt.length)return 1;if(this.length=0;r--){var i=0|this.words[r],n=0|t.words[r];if(i!==n){in&&(e=1);break}}return e},n.prototype.gtn=function(t){return 1===this.cmpn(t)},n.prototype.gt=function(t){return 1===this.cmp(t)},n.prototype.gten=function(t){return this.cmpn(t)>=0},n.prototype.gte=function(t){return this.cmp(t)>=0},n.prototype.ltn=function(t){return-1===this.cmpn(t)},n.prototype.lt=function(t){return-1===this.cmp(t)},n.prototype.lten=function(t){return this.cmpn(t)<=0},n.prototype.lte=function(t){return this.cmp(t)<=0},n.prototype.eqn=function(t){return 0===this.cmpn(t)},n.prototype.eq=function(t){return 0===this.cmp(t)},n.red=function(t){return new _(t)},n.prototype.toRed=function(t){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),t.convertTo(this)._forceRed(t)},n.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},n.prototype._forceRed=function(t){return this.red=t,this},n.prototype.forceRed=function(t){return r(!this.red,"Already a number in reduction context"),this._forceRed(t)},n.prototype.redAdd=function(t){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,t)},n.prototype.redIAdd=function(t){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,t)},n.prototype.redSub=function(t){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,t)},n.prototype.redISub=function(t){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,t)},n.prototype.redShl=function(t){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,t)},n.prototype.redMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.mul(this,t)},n.prototype.redIMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.imul(this,t)},n.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},n.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},n.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},n.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},n.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},n.prototype.redPow=function(t){return r(this.red&&!t.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,t)};var g={k256:null,p224:null,p192:null,p25519:null};function v(t,e){this.name=t,this.p=new n(e,16),this.n=this.p.bitLength(),this.k=new n(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function b(){v.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function y(){v.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function w(){v.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function M(){v.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function _(t){if("string"==typeof t){var e=n._prime(t);this.m=e.p,this.prime=e}else r(t.gtn(1),"modulus must be greater than 1"),this.m=t,this.prime=null}function A(t){_.call(this,t),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new n(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}v.prototype._tmp=function(){var t=new n(null);return t.words=new Array(Math.ceil(this.n/13)),t},v.prototype.ireduce=function(t){var e,r=t;do{this.split(r,this.tmp),e=(r=(r=this.imulK(r)).iadd(this.tmp)).bitLength()}while(e>this.n);var i=e0?r.isub(this.p):void 0!==r.strip?r.strip():r._strip(),r},v.prototype.split=function(t,e){t.iushrn(this.n,0,e)},v.prototype.imulK=function(t){return t.imul(this.k)},i(b,v),b.prototype.split=function(t,e){for(var r=4194303,i=Math.min(t.length,9),n=0;n>>22,o=s}o>>>=22,t.words[n-10]=o,0===o&&t.length>10?t.length-=10:t.length-=9},b.prototype.imulK=function(t){t.words[t.length]=0,t.words[t.length+1]=0,t.length+=2;for(var e=0,r=0;r>>=26,t.words[r]=n,e=i}return 0!==e&&(t.words[t.length++]=e),t},n._prime=function(t){if(g[t])return g[t];var e;if("k256"===t)e=new b;else if("p224"===t)e=new y;else if("p192"===t)e=new w;else{if("p25519"!==t)throw new Error("Unknown prime "+t);e=new M}return g[t]=e,e},_.prototype._verify1=function(t){r(0===t.negative,"red works only with positives"),r(t.red,"red works only with red numbers")},_.prototype._verify2=function(t,e){r(0==(t.negative|e.negative),"red works only with positives"),r(t.red&&t.red===e.red,"red works only with red numbers")},_.prototype.imod=function(t){return this.prime?this.prime.ireduce(t)._forceRed(this):t.umod(this.m)._forceRed(this)},_.prototype.neg=function(t){return t.isZero()?t.clone():this.m.sub(t)._forceRed(this)},_.prototype.add=function(t,e){this._verify2(t,e);var r=t.add(e);return r.cmp(this.m)>=0&&r.isub(this.m),r._forceRed(this)},_.prototype.iadd=function(t,e){this._verify2(t,e);var r=t.iadd(e);return r.cmp(this.m)>=0&&r.isub(this.m),r},_.prototype.sub=function(t,e){this._verify2(t,e);var r=t.sub(e);return r.cmpn(0)<0&&r.iadd(this.m),r._forceRed(this)},_.prototype.isub=function(t,e){this._verify2(t,e);var r=t.isub(e);return r.cmpn(0)<0&&r.iadd(this.m),r},_.prototype.shl=function(t,e){return this._verify1(t),this.imod(t.ushln(e))},_.prototype.imul=function(t,e){return this._verify2(t,e),this.imod(t.imul(e))},_.prototype.mul=function(t,e){return this._verify2(t,e),this.imod(t.mul(e))},_.prototype.isqr=function(t){return this.imul(t,t.clone())},_.prototype.sqr=function(t){return this.mul(t,t)},_.prototype.sqrt=function(t){if(t.isZero())return t.clone();var e=this.m.andln(3);if(r(e%2==1),3===e){var i=this.m.add(new n(1)).iushrn(2);return this.pow(t,i)}for(var o=this.m.subn(1),s=0;!o.isZero()&&0===o.andln(1);)s++,o.iushrn(1);r(!o.isZero());var a=new n(1).toRed(this),h=a.redNeg(),u=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new n(2*f*f).toRed(this);0!==this.pow(f,u).cmp(h);)f.redIAdd(h);for(var l=this.pow(f,o),d=this.pow(t,o.addn(1).iushrn(1)),c=this.pow(t,o),p=s;0!==c.cmp(a);){for(var m=c,g=0;0!==m.cmp(a);g++)m=m.redSqr();r(g=0;i--){for(var u=e.words[i],f=h-1;f>=0;f--){var l=u>>f&1;o!==r[0]&&(o=this.sqr(o)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===i&&0===f)&&(o=this.mul(o,r[s]),a=0,s=0)):a=0}h=26}return o},_.prototype.convertTo=function(t){var e=t.umod(this.m);return e===t?e.clone():e},_.prototype.convertFrom=function(t){var e=t.clone();return e.red=null,e},n.mont=function(t){return new A(t)},i(A,_),A.prototype.convertTo=function(t){return this.imod(t.ushln(this.shift))},A.prototype.convertFrom=function(t){var e=this.imod(t.mul(this.rinv));return e.red=null,e},A.prototype.imul=function(t,e){if(t.isZero()||e.isZero())return t.words[0]=0,t.length=1,t;var r=t.imul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),n=r.isub(i).iushrn(this.shift),o=n;return n.cmp(this.m)>=0?o=n.isub(this.m):n.cmpn(0)<0&&(o=n.iadd(this.m)),o._forceRed(this)},A.prototype.mul=function(t,e){if(t.isZero()||e.isZero())return new n(0)._forceRed(this);var r=t.mul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),o=r.isub(i).iushrn(this.shift),s=o;return o.cmp(this.m)>=0?s=o.isub(this.m):o.cmpn(0)<0&&(s=o.iadd(this.m)),s._forceRed(this)},A.prototype.invm=function(t){return this.imod(t._invmp(this.m).mul(this.r2))._forceRed(this)}}(void 0===t||t,this),t.exports}.call({})}var Bk,Rk,Tk,xk,Ik=!1;function Ck(t){this.curveType=xk[t],this.curveType||(this.curveType={name:t}),this.curve=new(C_().ec)(this.curveType.name),this.keys=void 0}function Pk(t,e,r){Array.isArray(t)||(t=t.toArray());var i=new Rk(t);if(r&&i.length=65&&r<=70?r-55:r>=97&&r<=102?r-87:r-48&15}function a(t,e,r){var i=s(t,r);return r-1>=e&&(i|=s(t,r-1)<<4),i}function h(t,e,r,i){for(var n=0,o=Math.min(t.length,r),s=e;s=49?a-49+10:a>=17?a-17+10:a}return n}n.isBN=function(t){return t instanceof n||null!==t&&"object"==typeof t&&t.constructor.wordSize===n.wordSize&&Array.isArray(t.words)},n.max=function(t,e){return t.cmp(e)>0?t:e},n.min=function(t,e){return t.cmp(e)<0?t:e},n.prototype._init=function(t,e,i){if("number"==typeof t)return this._initNumber(t,e,i);if("object"==typeof t)return this._initArray(t,e,i);"hex"===e&&(e=16),r(e===(0|e)&&e>=2&&e<=36);var n=0;"-"===(t=t.toString().replace(/\s+/g,""))[0]&&(n++,this.negative=1),n=0;n-=3)s=t[n]|t[n-1]<<8|t[n-2]<<16,this.words[o]|=s<>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);else if("le"===i)for(n=0,o=0;n>>26-a&67108863,(a+=24)>=26&&(a-=26,o++);return this.strip()},n.prototype._parseHex=function(t,e,r){this.length=Math.ceil((t.length-e)/6),this.words=new Array(this.length);for(var i=0;i=e;i-=2)n=a(t,e,i)<=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;else for(i=(t.length-e)%2==0?e+1:e;i=18?(o-=18,s+=1,this.words[s]|=n>>>26):o+=8;this.strip()},n.prototype._parseBase=function(t,e,r){this.words=[0],this.length=1;for(var i=0,n=1;n<=67108863;n*=e)i++;i--,n=n/e|0;for(var o=t.length-r,s=o%i,a=Math.min(o,o-s)+r,u=0,f=r;f1&&0===this.words[this.length-1];)this.length--;return this._normSign()},n.prototype._normSign=function(){return 1===this.length&&0===this.words[0]&&(this.negative=0),this},n.prototype.inspect=function(){return(this.red?""};var u=["","0","00","000","0000","00000","000000","0000000","00000000","000000000","0000000000","00000000000","000000000000","0000000000000","00000000000000","000000000000000","0000000000000000","00000000000000000","000000000000000000","0000000000000000000","00000000000000000000","000000000000000000000","0000000000000000000000","00000000000000000000000","000000000000000000000000","0000000000000000000000000"],f=[0,0,25,16,12,11,10,9,8,8,7,7,7,7,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5],l=[0,0,33554432,43046721,16777216,48828125,60466176,40353607,16777216,43046721,1e7,19487171,35831808,62748517,7529536,11390625,16777216,24137569,34012224,47045881,64e6,4084101,5153632,6436343,7962624,9765625,11881376,14348907,17210368,20511149,243e5,28629151,33554432,39135393,45435424,52521875,60466176];function d(t,e,r){r.negative=e.negative^t.negative;var i=t.length+e.length|0;r.length=i,i=i-1|0;var n=0|t.words[0],o=0|e.words[0],s=n*o,a=67108863&s,h=s/67108864|0;r.words[0]=a;for(var u=1;u>>26,l=67108863&h,d=Math.min(u,e.length-1),c=Math.max(0,u-t.length+1);c<=d;c++){var p=u-c|0;f+=(s=(n=0|t.words[p])*(o=0|e.words[c])+l)/67108864|0,l=67108863&s}r.words[u]=0|l,h=0|f}return 0!==h?r.words[u]=0|h:r.length--,r.strip()}n.prototype.toString=function(t,e){var i;if(e=0|e||1,16===(t=t||10)||"hex"===t){i="";for(var n=0,o=0,s=0;s>>24-n&16777215)||s!==this.length-1?u[6-h.length]+h+i:h+i,(n+=2)>=26&&(n-=26,s--)}for(0!==o&&(i=o.toString(16)+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}if(t===(0|t)&&t>=2&&t<=36){var d=f[t],c=l[t];i="";var p=this.clone();for(p.negative=0;!p.isZero();){var m=p.modn(c).toString(t);i=(p=p.idivn(c)).isZero()?m+i:u[d-m.length]+m+i}for(this.isZero()&&(i="0"+i);i.length%e!=0;)i="0"+i;return 0!==this.negative&&(i="-"+i),i}r(!1,"Base should be between 2 and 36")},n.prototype.toNumber=function(){var t=this.words[0];return 2===this.length?t+=67108864*this.words[1]:3===this.length&&1===this.words[2]?t+=4503599627370496+67108864*this.words[1]:this.length>2&&r(!1,"Number can only safely store up to 53 bits"),0!==this.negative?-t:t},n.prototype.toJSON=function(){return this.toString(16)},n.prototype.toBuffer=function(t,e){return r(void 0!==o),this.toArrayLike(o,t,e)},n.prototype.toArray=function(t,e){return this.toArrayLike(Array,t,e)},n.prototype.toArrayLike=function(t,e,i){var n=this.byteLength(),o=i||Math.max(1,n);r(n<=o,"byte array longer than desired length"),r(o>0,"Requested array length <= 0"),this.strip();var s,a,h="le"===e,u=new t(o),f=this.clone();if(h){for(a=0;!f.isZero();a++)s=f.andln(255),f.iushrn(8),u[a]=s;for(;a=4096&&(r+=13,e>>>=13),e>=64&&(r+=7,e>>>=7),e>=8&&(r+=4,e>>>=4),e>=2&&(r+=2,e>>>=2),r+e},n.prototype._zeroBits=function(t){if(0===t)return 26;var e=t,r=0;return 0==(8191&e)&&(r+=13,e>>>=13),0==(127&e)&&(r+=7,e>>>=7),0==(15&e)&&(r+=4,e>>>=4),0==(3&e)&&(r+=2,e>>>=2),0==(1&e)&&r++,r},n.prototype.bitLength=function(){var t=this.words[this.length-1],e=this._countBits(t);return 26*(this.length-1)+e},n.prototype.zeroBits=function(){if(this.isZero())return 0;for(var t=0,e=0;et.length?this.clone().ior(t):t.clone().ior(this)},n.prototype.uor=function(t){return this.length>t.length?this.clone().iuor(t):t.clone().iuor(this)},n.prototype.iuand=function(t){var e;e=this.length>t.length?t:this;for(var r=0;rt.length?this.clone().iand(t):t.clone().iand(this)},n.prototype.uand=function(t){return this.length>t.length?this.clone().iuand(t):t.clone().iuand(this)},n.prototype.iuxor=function(t){var e,r;this.length>t.length?(e=this,r=t):(e=t,r=this);for(var i=0;it.length?this.clone().ixor(t):t.clone().ixor(this)},n.prototype.uxor=function(t){return this.length>t.length?this.clone().iuxor(t):t.clone().iuxor(this)},n.prototype.inotn=function(t){r("number"==typeof t&&t>=0);var e=0|Math.ceil(t/26),i=t%26;this._expand(e),i>0&&e--;for(var n=0;n0&&(this.words[n]=~this.words[n]&67108863>>26-i),this.strip()},n.prototype.notn=function(t){return this.clone().inotn(t)},n.prototype.setn=function(t,e){r("number"==typeof t&&t>=0);var i=t/26|0,n=t%26;return this._expand(i+1),this.words[i]=e?this.words[i]|1<t.length?(r=this,i=t):(r=t,i=this);for(var n=0,o=0;o>>26;for(;0!==n&&o>>26;if(this.length=r.length,0!==n)this.words[this.length]=n,this.length++;else if(r!==this)for(;ot.length?this.clone().iadd(t):t.clone().iadd(this)},n.prototype.isub=function(t){if(0!==t.negative){t.negative=0;var e=this.iadd(t);return t.negative=1,e._normSign()}if(0!==this.negative)return this.negative=0,this.iadd(t),this.negative=1,this._normSign();var r,i,n=this.cmp(t);if(0===n)return this.negative=0,this.length=1,this.words[0]=0,this;n>0?(r=this,i=t):(r=t,i=this);for(var o=0,s=0;s>26,this.words[s]=67108863&e;for(;0!==o&&s>26,this.words[s]=67108863&e;if(0===o&&s>>13,c=0|s[1],p=8191&c,m=c>>>13,g=0|s[2],v=8191&g,b=g>>>13,y=0|s[3],w=8191&y,M=y>>>13,_=0|s[4],A=8191&_,E=_>>>13,S=0|s[5],k=8191&S,B=S>>>13,R=0|s[6],T=8191&R,x=R>>>13,I=0|s[7],C=8191&I,P=I>>>13,D=0|s[8],L=8191&D,O=D>>>13,j=0|s[9],U=8191&j,N=j>>>13,z=0|a[0],q=8191&z,F=z>>>13,Z=0|a[1],H=8191&Z,K=Z>>>13,W=0|a[2],G=8191&W,Y=W>>>13,V=0|a[3],$=8191&V,X=V>>>13,J=0|a[4],Q=8191&J,tt=J>>>13,et=0|a[5],rt=8191&et,it=et>>>13,nt=0|a[6],ot=8191&nt,st=nt>>>13,at=0|a[7],ht=8191&at,ut=at>>>13,ft=0|a[8],lt=8191&ft,dt=ft>>>13,ct=0|a[9],pt=8191&ct,mt=ct>>>13;r.negative=t.negative^e.negative,r.length=19;var gt=(u+(i=Math.imul(l,q))|0)+((8191&(n=(n=Math.imul(l,F))+Math.imul(d,q)|0))<<13)|0;u=((o=Math.imul(d,F))+(n>>>13)|0)+(gt>>>26)|0,gt&=67108863,i=Math.imul(p,q),n=(n=Math.imul(p,F))+Math.imul(m,q)|0,o=Math.imul(m,F);var vt=(u+(i=i+Math.imul(l,H)|0)|0)+((8191&(n=(n=n+Math.imul(l,K)|0)+Math.imul(d,H)|0))<<13)|0;u=((o=o+Math.imul(d,K)|0)+(n>>>13)|0)+(vt>>>26)|0,vt&=67108863,i=Math.imul(v,q),n=(n=Math.imul(v,F))+Math.imul(b,q)|0,o=Math.imul(b,F),i=i+Math.imul(p,H)|0,n=(n=n+Math.imul(p,K)|0)+Math.imul(m,H)|0,o=o+Math.imul(m,K)|0;var bt=(u+(i=i+Math.imul(l,G)|0)|0)+((8191&(n=(n=n+Math.imul(l,Y)|0)+Math.imul(d,G)|0))<<13)|0;u=((o=o+Math.imul(d,Y)|0)+(n>>>13)|0)+(bt>>>26)|0,bt&=67108863,i=Math.imul(w,q),n=(n=Math.imul(w,F))+Math.imul(M,q)|0,o=Math.imul(M,F),i=i+Math.imul(v,H)|0,n=(n=n+Math.imul(v,K)|0)+Math.imul(b,H)|0,o=o+Math.imul(b,K)|0,i=i+Math.imul(p,G)|0,n=(n=n+Math.imul(p,Y)|0)+Math.imul(m,G)|0,o=o+Math.imul(m,Y)|0;var yt=(u+(i=i+Math.imul(l,$)|0)|0)+((8191&(n=(n=n+Math.imul(l,X)|0)+Math.imul(d,$)|0))<<13)|0;u=((o=o+Math.imul(d,X)|0)+(n>>>13)|0)+(yt>>>26)|0,yt&=67108863,i=Math.imul(A,q),n=(n=Math.imul(A,F))+Math.imul(E,q)|0,o=Math.imul(E,F),i=i+Math.imul(w,H)|0,n=(n=n+Math.imul(w,K)|0)+Math.imul(M,H)|0,o=o+Math.imul(M,K)|0,i=i+Math.imul(v,G)|0,n=(n=n+Math.imul(v,Y)|0)+Math.imul(b,G)|0,o=o+Math.imul(b,Y)|0,i=i+Math.imul(p,$)|0,n=(n=n+Math.imul(p,X)|0)+Math.imul(m,$)|0,o=o+Math.imul(m,X)|0;var wt=(u+(i=i+Math.imul(l,Q)|0)|0)+((8191&(n=(n=n+Math.imul(l,tt)|0)+Math.imul(d,Q)|0))<<13)|0;u=((o=o+Math.imul(d,tt)|0)+(n>>>13)|0)+(wt>>>26)|0,wt&=67108863,i=Math.imul(k,q),n=(n=Math.imul(k,F))+Math.imul(B,q)|0,o=Math.imul(B,F),i=i+Math.imul(A,H)|0,n=(n=n+Math.imul(A,K)|0)+Math.imul(E,H)|0,o=o+Math.imul(E,K)|0,i=i+Math.imul(w,G)|0,n=(n=n+Math.imul(w,Y)|0)+Math.imul(M,G)|0,o=o+Math.imul(M,Y)|0,i=i+Math.imul(v,$)|0,n=(n=n+Math.imul(v,X)|0)+Math.imul(b,$)|0,o=o+Math.imul(b,X)|0,i=i+Math.imul(p,Q)|0,n=(n=n+Math.imul(p,tt)|0)+Math.imul(m,Q)|0,o=o+Math.imul(m,tt)|0;var Mt=(u+(i=i+Math.imul(l,rt)|0)|0)+((8191&(n=(n=n+Math.imul(l,it)|0)+Math.imul(d,rt)|0))<<13)|0;u=((o=o+Math.imul(d,it)|0)+(n>>>13)|0)+(Mt>>>26)|0,Mt&=67108863,i=Math.imul(T,q),n=(n=Math.imul(T,F))+Math.imul(x,q)|0,o=Math.imul(x,F),i=i+Math.imul(k,H)|0,n=(n=n+Math.imul(k,K)|0)+Math.imul(B,H)|0,o=o+Math.imul(B,K)|0,i=i+Math.imul(A,G)|0,n=(n=n+Math.imul(A,Y)|0)+Math.imul(E,G)|0,o=o+Math.imul(E,Y)|0,i=i+Math.imul(w,$)|0,n=(n=n+Math.imul(w,X)|0)+Math.imul(M,$)|0,o=o+Math.imul(M,X)|0,i=i+Math.imul(v,Q)|0,n=(n=n+Math.imul(v,tt)|0)+Math.imul(b,Q)|0,o=o+Math.imul(b,tt)|0,i=i+Math.imul(p,rt)|0,n=(n=n+Math.imul(p,it)|0)+Math.imul(m,rt)|0,o=o+Math.imul(m,it)|0;var _t=(u+(i=i+Math.imul(l,ot)|0)|0)+((8191&(n=(n=n+Math.imul(l,st)|0)+Math.imul(d,ot)|0))<<13)|0;u=((o=o+Math.imul(d,st)|0)+(n>>>13)|0)+(_t>>>26)|0,_t&=67108863,i=Math.imul(C,q),n=(n=Math.imul(C,F))+Math.imul(P,q)|0,o=Math.imul(P,F),i=i+Math.imul(T,H)|0,n=(n=n+Math.imul(T,K)|0)+Math.imul(x,H)|0,o=o+Math.imul(x,K)|0,i=i+Math.imul(k,G)|0,n=(n=n+Math.imul(k,Y)|0)+Math.imul(B,G)|0,o=o+Math.imul(B,Y)|0,i=i+Math.imul(A,$)|0,n=(n=n+Math.imul(A,X)|0)+Math.imul(E,$)|0,o=o+Math.imul(E,X)|0,i=i+Math.imul(w,Q)|0,n=(n=n+Math.imul(w,tt)|0)+Math.imul(M,Q)|0,o=o+Math.imul(M,tt)|0,i=i+Math.imul(v,rt)|0,n=(n=n+Math.imul(v,it)|0)+Math.imul(b,rt)|0,o=o+Math.imul(b,it)|0,i=i+Math.imul(p,ot)|0,n=(n=n+Math.imul(p,st)|0)+Math.imul(m,ot)|0,o=o+Math.imul(m,st)|0;var At=(u+(i=i+Math.imul(l,ht)|0)|0)+((8191&(n=(n=n+Math.imul(l,ut)|0)+Math.imul(d,ht)|0))<<13)|0;u=((o=o+Math.imul(d,ut)|0)+(n>>>13)|0)+(At>>>26)|0,At&=67108863,i=Math.imul(L,q),n=(n=Math.imul(L,F))+Math.imul(O,q)|0,o=Math.imul(O,F),i=i+Math.imul(C,H)|0,n=(n=n+Math.imul(C,K)|0)+Math.imul(P,H)|0,o=o+Math.imul(P,K)|0,i=i+Math.imul(T,G)|0,n=(n=n+Math.imul(T,Y)|0)+Math.imul(x,G)|0,o=o+Math.imul(x,Y)|0,i=i+Math.imul(k,$)|0,n=(n=n+Math.imul(k,X)|0)+Math.imul(B,$)|0,o=o+Math.imul(B,X)|0,i=i+Math.imul(A,Q)|0,n=(n=n+Math.imul(A,tt)|0)+Math.imul(E,Q)|0,o=o+Math.imul(E,tt)|0,i=i+Math.imul(w,rt)|0,n=(n=n+Math.imul(w,it)|0)+Math.imul(M,rt)|0,o=o+Math.imul(M,it)|0,i=i+Math.imul(v,ot)|0,n=(n=n+Math.imul(v,st)|0)+Math.imul(b,ot)|0,o=o+Math.imul(b,st)|0,i=i+Math.imul(p,ht)|0,n=(n=n+Math.imul(p,ut)|0)+Math.imul(m,ht)|0,o=o+Math.imul(m,ut)|0;var Et=(u+(i=i+Math.imul(l,lt)|0)|0)+((8191&(n=(n=n+Math.imul(l,dt)|0)+Math.imul(d,lt)|0))<<13)|0;u=((o=o+Math.imul(d,dt)|0)+(n>>>13)|0)+(Et>>>26)|0,Et&=67108863,i=Math.imul(U,q),n=(n=Math.imul(U,F))+Math.imul(N,q)|0,o=Math.imul(N,F),i=i+Math.imul(L,H)|0,n=(n=n+Math.imul(L,K)|0)+Math.imul(O,H)|0,o=o+Math.imul(O,K)|0,i=i+Math.imul(C,G)|0,n=(n=n+Math.imul(C,Y)|0)+Math.imul(P,G)|0,o=o+Math.imul(P,Y)|0,i=i+Math.imul(T,$)|0,n=(n=n+Math.imul(T,X)|0)+Math.imul(x,$)|0,o=o+Math.imul(x,X)|0,i=i+Math.imul(k,Q)|0,n=(n=n+Math.imul(k,tt)|0)+Math.imul(B,Q)|0,o=o+Math.imul(B,tt)|0,i=i+Math.imul(A,rt)|0,n=(n=n+Math.imul(A,it)|0)+Math.imul(E,rt)|0,o=o+Math.imul(E,it)|0,i=i+Math.imul(w,ot)|0,n=(n=n+Math.imul(w,st)|0)+Math.imul(M,ot)|0,o=o+Math.imul(M,st)|0,i=i+Math.imul(v,ht)|0,n=(n=n+Math.imul(v,ut)|0)+Math.imul(b,ht)|0,o=o+Math.imul(b,ut)|0,i=i+Math.imul(p,lt)|0,n=(n=n+Math.imul(p,dt)|0)+Math.imul(m,lt)|0,o=o+Math.imul(m,dt)|0;var St=(u+(i=i+Math.imul(l,pt)|0)|0)+((8191&(n=(n=n+Math.imul(l,mt)|0)+Math.imul(d,pt)|0))<<13)|0;u=((o=o+Math.imul(d,mt)|0)+(n>>>13)|0)+(St>>>26)|0,St&=67108863,i=Math.imul(U,H),n=(n=Math.imul(U,K))+Math.imul(N,H)|0,o=Math.imul(N,K),i=i+Math.imul(L,G)|0,n=(n=n+Math.imul(L,Y)|0)+Math.imul(O,G)|0,o=o+Math.imul(O,Y)|0,i=i+Math.imul(C,$)|0,n=(n=n+Math.imul(C,X)|0)+Math.imul(P,$)|0,o=o+Math.imul(P,X)|0,i=i+Math.imul(T,Q)|0,n=(n=n+Math.imul(T,tt)|0)+Math.imul(x,Q)|0,o=o+Math.imul(x,tt)|0,i=i+Math.imul(k,rt)|0,n=(n=n+Math.imul(k,it)|0)+Math.imul(B,rt)|0,o=o+Math.imul(B,it)|0,i=i+Math.imul(A,ot)|0,n=(n=n+Math.imul(A,st)|0)+Math.imul(E,ot)|0,o=o+Math.imul(E,st)|0,i=i+Math.imul(w,ht)|0,n=(n=n+Math.imul(w,ut)|0)+Math.imul(M,ht)|0,o=o+Math.imul(M,ut)|0,i=i+Math.imul(v,lt)|0,n=(n=n+Math.imul(v,dt)|0)+Math.imul(b,lt)|0,o=o+Math.imul(b,dt)|0;var kt=(u+(i=i+Math.imul(p,pt)|0)|0)+((8191&(n=(n=n+Math.imul(p,mt)|0)+Math.imul(m,pt)|0))<<13)|0;u=((o=o+Math.imul(m,mt)|0)+(n>>>13)|0)+(kt>>>26)|0,kt&=67108863,i=Math.imul(U,G),n=(n=Math.imul(U,Y))+Math.imul(N,G)|0,o=Math.imul(N,Y),i=i+Math.imul(L,$)|0,n=(n=n+Math.imul(L,X)|0)+Math.imul(O,$)|0,o=o+Math.imul(O,X)|0,i=i+Math.imul(C,Q)|0,n=(n=n+Math.imul(C,tt)|0)+Math.imul(P,Q)|0,o=o+Math.imul(P,tt)|0,i=i+Math.imul(T,rt)|0,n=(n=n+Math.imul(T,it)|0)+Math.imul(x,rt)|0,o=o+Math.imul(x,it)|0,i=i+Math.imul(k,ot)|0,n=(n=n+Math.imul(k,st)|0)+Math.imul(B,ot)|0,o=o+Math.imul(B,st)|0,i=i+Math.imul(A,ht)|0,n=(n=n+Math.imul(A,ut)|0)+Math.imul(E,ht)|0,o=o+Math.imul(E,ut)|0,i=i+Math.imul(w,lt)|0,n=(n=n+Math.imul(w,dt)|0)+Math.imul(M,lt)|0,o=o+Math.imul(M,dt)|0;var Bt=(u+(i=i+Math.imul(v,pt)|0)|0)+((8191&(n=(n=n+Math.imul(v,mt)|0)+Math.imul(b,pt)|0))<<13)|0;u=((o=o+Math.imul(b,mt)|0)+(n>>>13)|0)+(Bt>>>26)|0,Bt&=67108863,i=Math.imul(U,$),n=(n=Math.imul(U,X))+Math.imul(N,$)|0,o=Math.imul(N,X),i=i+Math.imul(L,Q)|0,n=(n=n+Math.imul(L,tt)|0)+Math.imul(O,Q)|0,o=o+Math.imul(O,tt)|0,i=i+Math.imul(C,rt)|0,n=(n=n+Math.imul(C,it)|0)+Math.imul(P,rt)|0,o=o+Math.imul(P,it)|0,i=i+Math.imul(T,ot)|0,n=(n=n+Math.imul(T,st)|0)+Math.imul(x,ot)|0,o=o+Math.imul(x,st)|0,i=i+Math.imul(k,ht)|0,n=(n=n+Math.imul(k,ut)|0)+Math.imul(B,ht)|0,o=o+Math.imul(B,ut)|0,i=i+Math.imul(A,lt)|0,n=(n=n+Math.imul(A,dt)|0)+Math.imul(E,lt)|0,o=o+Math.imul(E,dt)|0;var Rt=(u+(i=i+Math.imul(w,pt)|0)|0)+((8191&(n=(n=n+Math.imul(w,mt)|0)+Math.imul(M,pt)|0))<<13)|0;u=((o=o+Math.imul(M,mt)|0)+(n>>>13)|0)+(Rt>>>26)|0,Rt&=67108863,i=Math.imul(U,Q),n=(n=Math.imul(U,tt))+Math.imul(N,Q)|0,o=Math.imul(N,tt),i=i+Math.imul(L,rt)|0,n=(n=n+Math.imul(L,it)|0)+Math.imul(O,rt)|0,o=o+Math.imul(O,it)|0,i=i+Math.imul(C,ot)|0,n=(n=n+Math.imul(C,st)|0)+Math.imul(P,ot)|0,o=o+Math.imul(P,st)|0,i=i+Math.imul(T,ht)|0,n=(n=n+Math.imul(T,ut)|0)+Math.imul(x,ht)|0,o=o+Math.imul(x,ut)|0,i=i+Math.imul(k,lt)|0,n=(n=n+Math.imul(k,dt)|0)+Math.imul(B,lt)|0,o=o+Math.imul(B,dt)|0;var Tt=(u+(i=i+Math.imul(A,pt)|0)|0)+((8191&(n=(n=n+Math.imul(A,mt)|0)+Math.imul(E,pt)|0))<<13)|0;u=((o=o+Math.imul(E,mt)|0)+(n>>>13)|0)+(Tt>>>26)|0,Tt&=67108863,i=Math.imul(U,rt),n=(n=Math.imul(U,it))+Math.imul(N,rt)|0,o=Math.imul(N,it),i=i+Math.imul(L,ot)|0,n=(n=n+Math.imul(L,st)|0)+Math.imul(O,ot)|0,o=o+Math.imul(O,st)|0,i=i+Math.imul(C,ht)|0,n=(n=n+Math.imul(C,ut)|0)+Math.imul(P,ht)|0,o=o+Math.imul(P,ut)|0,i=i+Math.imul(T,lt)|0,n=(n=n+Math.imul(T,dt)|0)+Math.imul(x,lt)|0,o=o+Math.imul(x,dt)|0;var xt=(u+(i=i+Math.imul(k,pt)|0)|0)+((8191&(n=(n=n+Math.imul(k,mt)|0)+Math.imul(B,pt)|0))<<13)|0;u=((o=o+Math.imul(B,mt)|0)+(n>>>13)|0)+(xt>>>26)|0,xt&=67108863,i=Math.imul(U,ot),n=(n=Math.imul(U,st))+Math.imul(N,ot)|0,o=Math.imul(N,st),i=i+Math.imul(L,ht)|0,n=(n=n+Math.imul(L,ut)|0)+Math.imul(O,ht)|0,o=o+Math.imul(O,ut)|0,i=i+Math.imul(C,lt)|0,n=(n=n+Math.imul(C,dt)|0)+Math.imul(P,lt)|0,o=o+Math.imul(P,dt)|0;var It=(u+(i=i+Math.imul(T,pt)|0)|0)+((8191&(n=(n=n+Math.imul(T,mt)|0)+Math.imul(x,pt)|0))<<13)|0;u=((o=o+Math.imul(x,mt)|0)+(n>>>13)|0)+(It>>>26)|0,It&=67108863,i=Math.imul(U,ht),n=(n=Math.imul(U,ut))+Math.imul(N,ht)|0,o=Math.imul(N,ut),i=i+Math.imul(L,lt)|0,n=(n=n+Math.imul(L,dt)|0)+Math.imul(O,lt)|0,o=o+Math.imul(O,dt)|0;var Ct=(u+(i=i+Math.imul(C,pt)|0)|0)+((8191&(n=(n=n+Math.imul(C,mt)|0)+Math.imul(P,pt)|0))<<13)|0;u=((o=o+Math.imul(P,mt)|0)+(n>>>13)|0)+(Ct>>>26)|0,Ct&=67108863,i=Math.imul(U,lt),n=(n=Math.imul(U,dt))+Math.imul(N,lt)|0,o=Math.imul(N,dt);var Pt=(u+(i=i+Math.imul(L,pt)|0)|0)+((8191&(n=(n=n+Math.imul(L,mt)|0)+Math.imul(O,pt)|0))<<13)|0;u=((o=o+Math.imul(O,mt)|0)+(n>>>13)|0)+(Pt>>>26)|0,Pt&=67108863;var Dt=(u+(i=Math.imul(U,pt))|0)+((8191&(n=(n=Math.imul(U,mt))+Math.imul(N,pt)|0))<<13)|0;return u=((o=Math.imul(N,mt))+(n>>>13)|0)+(Dt>>>26)|0,Dt&=67108863,h[0]=gt,h[1]=vt,h[2]=bt,h[3]=yt,h[4]=wt,h[5]=Mt,h[6]=_t,h[7]=At,h[8]=Et,h[9]=St,h[10]=kt,h[11]=Bt,h[12]=Rt,h[13]=Tt,h[14]=xt,h[15]=It,h[16]=Ct,h[17]=Pt,h[18]=Dt,0!==u&&(h[19]=u,r.length++),r};function p(t,e,r){return(new m).mulp(t,e,r)}function m(t,e){this.x=t,this.y=e}Math.imul||(c=d),n.prototype.mulTo=function(t,e){var r,i=this.length+t.length;return r=10===this.length&&10===t.length?c(this,t,e):i<63?d(this,t,e):i<1024?function(t,e,r){r.negative=e.negative^t.negative,r.length=t.length+e.length;for(var i=0,n=0,o=0;o>>26)|0)>>>26,s&=67108863}r.words[o]=a,i=s,s=n}return 0!==i?r.words[o]=i:r.length--,r.strip()}(this,t,e):p(this,t,e),r},m.prototype.makeRBT=function(t){for(var e=new Array(t),r=n.prototype._countBits(t)-1,i=0;i>=1;return i},m.prototype.permute=function(t,e,r,i,n,o){for(var s=0;s>>=1)n++;return 1<>>=13,i[2*s+1]=8191&o,o>>>=13;for(s=2*e;s>=26,e+=n/67108864|0,e+=o>>>26,this.words[i]=67108863&o}return 0!==e&&(this.words[i]=e,this.length++),this},n.prototype.muln=function(t){return this.clone().imuln(t)},n.prototype.sqr=function(){return this.mul(this)},n.prototype.isqr=function(){return this.imul(this.clone())},n.prototype.pow=function(t){var e=function(t){for(var e=new Array(t.bitLength()),r=0;r>>n}return e}(t);if(0===e.length)return new n(1);for(var r=this,i=0;i=0);var e,i=t%26,n=(t-i)/26,o=67108863>>>26-i<<26-i;if(0!==i){var s=0;for(e=0;e>>26-i}s&&(this.words[e]=s,this.length++)}if(0!==n){for(e=this.length-1;e>=0;e--)this.words[e+n]=this.words[e];for(e=0;e=0),n=e?(e-e%26)/26:0;var o=t%26,s=Math.min((t-o)/26,this.length),a=67108863^67108863>>>o<s)for(this.length-=s,u=0;u=0&&(0!==f||u>=n);u--){var l=0|this.words[u];this.words[u]=f<<26-o|l>>>o,f=l&a}return h&&0!==f&&(h.words[h.length++]=f),0===this.length&&(this.words[0]=0,this.length=1),this.strip()},n.prototype.ishrn=function(t,e,i){return r(0===this.negative),this.iushrn(t,e,i)},n.prototype.shln=function(t){return this.clone().ishln(t)},n.prototype.ushln=function(t){return this.clone().iushln(t)},n.prototype.shrn=function(t){return this.clone().ishrn(t)},n.prototype.ushrn=function(t){return this.clone().iushrn(t)},n.prototype.testn=function(t){r("number"==typeof t&&t>=0);var e=t%26,i=(t-e)/26,n=1<=0);var e=t%26,i=(t-e)/26;if(r(0===this.negative,"imaskn works only with positive numbers"),this.length<=i)return this;if(0!==e&&i++,this.length=Math.min(i,this.length),0!==e){var n=67108863^67108863>>>e<=67108864;e++)this.words[e]-=67108864,e===this.length-1?this.words[e+1]=1:this.words[e+1]++;return this.length=Math.max(this.length,e+1),this},n.prototype.isubn=function(t){if(r("number"==typeof t),r(t<67108864),t<0)return this.iaddn(-t);if(0!==this.negative)return this.negative=0,this.iaddn(t),this.negative=1,this;if(this.words[0]-=t,1===this.length&&this.words[0]<0)this.words[0]=-this.words[0],this.negative=1;else for(var e=0;e>26)-(h/67108864|0),this.words[n+i]=67108863&o}for(;n>26,this.words[n+i]=67108863&o;if(0===a)return this.strip();for(r(-1===a),a=0,n=0;n>26,this.words[n]=67108863&o;return this.negative=1,this.strip()},n.prototype._wordDiv=function(t,e){var r=(this.length,t.length),i=this.clone(),o=t,s=0|o.words[o.length-1];0!==(r=26-this._countBits(s))&&(o=o.ushln(r),i.iushln(r),s=0|o.words[o.length-1]);var a,h=i.length-o.length;if("mod"!==e){(a=new n(null)).length=h+1,a.words=new Array(a.length);for(var u=0;u=0;l--){var d=67108864*(0|i.words[o.length+l])+(0|i.words[o.length+l-1]);for(d=Math.min(d/s|0,67108863),i._ishlnsubmul(o,d,l);0!==i.negative;)d--,i.negative=0,i._ishlnsubmul(o,1,l),i.isZero()||(i.negative^=1);a&&(a.words[l]=d)}return a&&a.strip(),i.strip(),"div"!==e&&0!==r&&i.iushrn(r),{div:a||null,mod:i}},n.prototype.divmod=function(t,e,i){return r(!t.isZero()),this.isZero()?{div:new n(0),mod:new n(0)}:0!==this.negative&&0===t.negative?(a=this.neg().divmod(t,e),"mod"!==e&&(o=a.div.neg()),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.iadd(t)),{div:o,mod:s}):0===this.negative&&0!==t.negative?(a=this.divmod(t.neg(),e),"mod"!==e&&(o=a.div.neg()),{div:o,mod:a.mod}):0!=(this.negative&t.negative)?(a=this.neg().divmod(t.neg(),e),"div"!==e&&(s=a.mod.neg(),i&&0!==s.negative&&s.isub(t)),{div:a.div,mod:s}):t.length>this.length||this.cmp(t)<0?{div:new n(0),mod:this}:1===t.length?"div"===e?{div:this.divn(t.words[0]),mod:null}:"mod"===e?{div:null,mod:new n(this.modn(t.words[0]))}:{div:this.divn(t.words[0]),mod:new n(this.modn(t.words[0]))}:this._wordDiv(t,e);var o,s,a},n.prototype.div=function(t){return this.divmod(t,"div",!1).div},n.prototype.mod=function(t){return this.divmod(t,"mod",!1).mod},n.prototype.umod=function(t){return this.divmod(t,"mod",!0).mod},n.prototype.divRound=function(t){var e=this.divmod(t);if(e.mod.isZero())return e.div;var r=0!==e.div.negative?e.mod.isub(t):e.mod,i=t.ushrn(1),n=t.andln(1),o=r.cmp(i);return o<0||1===n&&0===o?e.div:0!==e.div.negative?e.div.isubn(1):e.div.iaddn(1)},n.prototype.modn=function(t){r(t<=67108863);for(var e=(1<<26)%t,i=0,n=this.length-1;n>=0;n--)i=(e*i+(0|this.words[n]))%t;return i},n.prototype.idivn=function(t){r(t<=67108863);for(var e=0,i=this.length-1;i>=0;i--){var n=(0|this.words[i])+67108864*e;this.words[i]=n/t|0,e=n%t}return this.strip()},n.prototype.divn=function(t){return this.clone().idivn(t)},n.prototype.egcd=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o=new n(1),s=new n(0),a=new n(0),h=new n(1),u=0;e.isEven()&&i.isEven();)e.iushrn(1),i.iushrn(1),++u;for(var f=i.clone(),l=e.clone();!e.isZero();){for(var d=0,c=1;0==(e.words[0]&c)&&d<26;++d,c<<=1);if(d>0)for(e.iushrn(d);d-- >0;)(o.isOdd()||s.isOdd())&&(o.iadd(f),s.isub(l)),o.iushrn(1),s.iushrn(1);for(var p=0,m=1;0==(i.words[0]&m)&&p<26;++p,m<<=1);if(p>0)for(i.iushrn(p);p-- >0;)(a.isOdd()||h.isOdd())&&(a.iadd(f),h.isub(l)),a.iushrn(1),h.iushrn(1);e.cmp(i)>=0?(e.isub(i),o.isub(a),s.isub(h)):(i.isub(e),a.isub(o),h.isub(s))}return{a:a,b:h,gcd:i.iushln(u)}},n.prototype._invmp=function(t){r(0===t.negative),r(!t.isZero());var e=this,i=t.clone();e=0!==e.negative?e.umod(t):e.clone();for(var o,s=new n(1),a=new n(0),h=i.clone();e.cmpn(1)>0&&i.cmpn(1)>0;){for(var u=0,f=1;0==(e.words[0]&f)&&u<26;++u,f<<=1);if(u>0)for(e.iushrn(u);u-- >0;)s.isOdd()&&s.iadd(h),s.iushrn(1);for(var l=0,d=1;0==(i.words[0]&d)&&l<26;++l,d<<=1);if(l>0)for(i.iushrn(l);l-- >0;)a.isOdd()&&a.iadd(h),a.iushrn(1);e.cmp(i)>=0?(e.isub(i),s.isub(a)):(i.isub(e),a.isub(s))}return(o=0===e.cmpn(1)?s:a).cmpn(0)<0&&o.iadd(t),o},n.prototype.gcd=function(t){if(this.isZero())return t.abs();if(t.isZero())return this.abs();var e=this.clone(),r=t.clone();e.negative=0,r.negative=0;for(var i=0;e.isEven()&&r.isEven();i++)e.iushrn(1),r.iushrn(1);for(;;){for(;e.isEven();)e.iushrn(1);for(;r.isEven();)r.iushrn(1);var n=e.cmp(r);if(n<0){var o=e;e=r,r=o}else if(0===n||0===r.cmpn(1))break;e.isub(r)}return r.iushln(i)},n.prototype.invm=function(t){return this.egcd(t).a.umod(t)},n.prototype.isEven=function(){return 0==(1&this.words[0])},n.prototype.isOdd=function(){return 1==(1&this.words[0])},n.prototype.andln=function(t){return this.words[0]&t},n.prototype.bincn=function(t){r("number"==typeof t);var e=t%26,i=(t-e)/26,n=1<>>26,a&=67108863,this.words[s]=a}return 0!==o&&(this.words[s]=o,this.length++),this},n.prototype.isZero=function(){return 1===this.length&&0===this.words[0]},n.prototype.cmpn=function(t){var e,i=t<0;if(0!==this.negative&&!i)return-1;if(0===this.negative&&i)return 1;if(this.strip(),this.length>1)e=1;else{i&&(t=-t),r(t<=67108863,"Number is too big");var n=0|this.words[0];e=n===t?0:nt.length)return 1;if(this.length=0;r--){var i=0|this.words[r],n=0|t.words[r];if(i!==n){in&&(e=1);break}}return e},n.prototype.gtn=function(t){return 1===this.cmpn(t)},n.prototype.gt=function(t){return 1===this.cmp(t)},n.prototype.gten=function(t){return this.cmpn(t)>=0},n.prototype.gte=function(t){return this.cmp(t)>=0},n.prototype.ltn=function(t){return-1===this.cmpn(t)},n.prototype.lt=function(t){return-1===this.cmp(t)},n.prototype.lten=function(t){return this.cmpn(t)<=0},n.prototype.lte=function(t){return this.cmp(t)<=0},n.prototype.eqn=function(t){return 0===this.cmpn(t)},n.prototype.eq=function(t){return 0===this.cmp(t)},n.red=function(t){return new _(t)},n.prototype.toRed=function(t){return r(!this.red,"Already a number in reduction context"),r(0===this.negative,"red works only with positives"),t.convertTo(this)._forceRed(t)},n.prototype.fromRed=function(){return r(this.red,"fromRed works only with numbers in reduction context"),this.red.convertFrom(this)},n.prototype._forceRed=function(t){return this.red=t,this},n.prototype.forceRed=function(t){return r(!this.red,"Already a number in reduction context"),this._forceRed(t)},n.prototype.redAdd=function(t){return r(this.red,"redAdd works only with red numbers"),this.red.add(this,t)},n.prototype.redIAdd=function(t){return r(this.red,"redIAdd works only with red numbers"),this.red.iadd(this,t)},n.prototype.redSub=function(t){return r(this.red,"redSub works only with red numbers"),this.red.sub(this,t)},n.prototype.redISub=function(t){return r(this.red,"redISub works only with red numbers"),this.red.isub(this,t)},n.prototype.redShl=function(t){return r(this.red,"redShl works only with red numbers"),this.red.shl(this,t)},n.prototype.redMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.mul(this,t)},n.prototype.redIMul=function(t){return r(this.red,"redMul works only with red numbers"),this.red._verify2(this,t),this.red.imul(this,t)},n.prototype.redSqr=function(){return r(this.red,"redSqr works only with red numbers"),this.red._verify1(this),this.red.sqr(this)},n.prototype.redISqr=function(){return r(this.red,"redISqr works only with red numbers"),this.red._verify1(this),this.red.isqr(this)},n.prototype.redSqrt=function(){return r(this.red,"redSqrt works only with red numbers"),this.red._verify1(this),this.red.sqrt(this)},n.prototype.redInvm=function(){return r(this.red,"redInvm works only with red numbers"),this.red._verify1(this),this.red.invm(this)},n.prototype.redNeg=function(){return r(this.red,"redNeg works only with red numbers"),this.red._verify1(this),this.red.neg(this)},n.prototype.redPow=function(t){return r(this.red&&!t.red,"redPow(normalNum)"),this.red._verify1(this),this.red.pow(this,t)};var g={k256:null,p224:null,p192:null,p25519:null};function v(t,e){this.name=t,this.p=new n(e,16),this.n=this.p.bitLength(),this.k=new n(1).iushln(this.n).isub(this.p),this.tmp=this._tmp()}function b(){v.call(this,"k256","ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f")}function y(){v.call(this,"p224","ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001")}function w(){v.call(this,"p192","ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff")}function M(){v.call(this,"25519","7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed")}function _(t){if("string"==typeof t){var e=n._prime(t);this.m=e.p,this.prime=e}else r(t.gtn(1),"modulus must be greater than 1"),this.m=t,this.prime=null}function A(t){_.call(this,t),this.shift=this.m.bitLength(),this.shift%26!=0&&(this.shift+=26-this.shift%26),this.r=new n(1).iushln(this.shift),this.r2=this.imod(this.r.sqr()),this.rinv=this.r._invmp(this.m),this.minv=this.rinv.mul(this.r).isubn(1).div(this.m),this.minv=this.minv.umod(this.r),this.minv=this.r.sub(this.minv)}v.prototype._tmp=function(){var t=new n(null);return t.words=new Array(Math.ceil(this.n/13)),t},v.prototype.ireduce=function(t){var e,r=t;do{this.split(r,this.tmp),e=(r=(r=this.imulK(r)).iadd(this.tmp)).bitLength()}while(e>this.n);var i=e0?r.isub(this.p):void 0!==r.strip?r.strip():r._strip(),r},v.prototype.split=function(t,e){t.iushrn(this.n,0,e)},v.prototype.imulK=function(t){return t.imul(this.k)},i(b,v),b.prototype.split=function(t,e){for(var r=4194303,i=Math.min(t.length,9),n=0;n>>22,o=s}o>>>=22,t.words[n-10]=o,0===o&&t.length>10?t.length-=10:t.length-=9},b.prototype.imulK=function(t){t.words[t.length]=0,t.words[t.length+1]=0,t.length+=2;for(var e=0,r=0;r>>=26,t.words[r]=n,e=i}return 0!==e&&(t.words[t.length++]=e),t},n._prime=function(t){if(g[t])return g[t];var e;if("k256"===t)e=new b;else if("p224"===t)e=new y;else if("p192"===t)e=new w;else{if("p25519"!==t)throw new Error("Unknown prime "+t);e=new M}return g[t]=e,e},_.prototype._verify1=function(t){r(0===t.negative,"red works only with positives"),r(t.red,"red works only with red numbers")},_.prototype._verify2=function(t,e){r(0==(t.negative|e.negative),"red works only with positives"),r(t.red&&t.red===e.red,"red works only with red numbers")},_.prototype.imod=function(t){return this.prime?this.prime.ireduce(t)._forceRed(this):t.umod(this.m)._forceRed(this)},_.prototype.neg=function(t){return t.isZero()?t.clone():this.m.sub(t)._forceRed(this)},_.prototype.add=function(t,e){this._verify2(t,e);var r=t.add(e);return r.cmp(this.m)>=0&&r.isub(this.m),r._forceRed(this)},_.prototype.iadd=function(t,e){this._verify2(t,e);var r=t.iadd(e);return r.cmp(this.m)>=0&&r.isub(this.m),r},_.prototype.sub=function(t,e){this._verify2(t,e);var r=t.sub(e);return r.cmpn(0)<0&&r.iadd(this.m),r._forceRed(this)},_.prototype.isub=function(t,e){this._verify2(t,e);var r=t.isub(e);return r.cmpn(0)<0&&r.iadd(this.m),r},_.prototype.shl=function(t,e){return this._verify1(t),this.imod(t.ushln(e))},_.prototype.imul=function(t,e){return this._verify2(t,e),this.imod(t.imul(e))},_.prototype.mul=function(t,e){return this._verify2(t,e),this.imod(t.mul(e))},_.prototype.isqr=function(t){return this.imul(t,t.clone())},_.prototype.sqr=function(t){return this.mul(t,t)},_.prototype.sqrt=function(t){if(t.isZero())return t.clone();var e=this.m.andln(3);if(r(e%2==1),3===e){var i=this.m.add(new n(1)).iushrn(2);return this.pow(t,i)}for(var o=this.m.subn(1),s=0;!o.isZero()&&0===o.andln(1);)s++,o.iushrn(1);r(!o.isZero());var a=new n(1).toRed(this),h=a.redNeg(),u=this.m.subn(1).iushrn(1),f=this.m.bitLength();for(f=new n(2*f*f).toRed(this);0!==this.pow(f,u).cmp(h);)f.redIAdd(h);for(var l=this.pow(f,o),d=this.pow(t,o.addn(1).iushrn(1)),c=this.pow(t,o),p=s;0!==c.cmp(a);){for(var m=c,g=0;0!==m.cmp(a);g++)m=m.redSqr();r(g=0;i--){for(var u=e.words[i],f=h-1;f>=0;f--){var l=u>>f&1;o!==r[0]&&(o=this.sqr(o)),0!==l||0!==s?(s<<=1,s|=l,(4===++a||0===i&&0===f)&&(o=this.mul(o,r[s]),a=0,s=0)):a=0}h=26}return o},_.prototype.convertTo=function(t){var e=t.umod(this.m);return e===t?e.clone():e},_.prototype.convertFrom=function(t){var e=t.clone();return e.red=null,e},n.mont=function(t){return new A(t)},i(A,_),A.prototype.convertTo=function(t){return this.imod(t.ushln(this.shift))},A.prototype.convertFrom=function(t){var e=this.imod(t.mul(this.rinv));return e.red=null,e},A.prototype.imul=function(t,e){if(t.isZero()||e.isZero())return t.words[0]=0,t.length=1,t;var r=t.imul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),n=r.isub(i).iushrn(this.shift),o=n;return n.cmp(this.m)>=0?o=n.isub(this.m):n.cmpn(0)<0&&(o=n.iadd(this.m)),o._forceRed(this)},A.prototype.mul=function(t,e){if(t.isZero()||e.isZero())return new n(0)._forceRed(this);var r=t.mul(e),i=r.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m),o=r.isub(i).iushrn(this.shift),s=o;return o.cmp(this.m)>=0?s=o.isub(this.m):o.cmpn(0)<0&&(s=o.iadd(this.m)),s._forceRed(this)},A.prototype.invm=function(t){return this.imod(t._invmp(this.m).mul(this.r2))._forceRed(this)}}(void 0===t||t,this),t.exports}.call({})}function Gk(){return Kk||(Kk=!0,Wk()),Hk}var Yk,Vk,$k,Xk=!1;function Jk(t,e){return $k.from(t.toRed(Vk.mont(e.modulus)).redPow(new Vk(e.publicExponent)).fromRed().toArray())}function Qk(){return Xk||(Xk=!0,Yk={},Vk=Gk(),$k=lr().Buffer,Yk=Jk),Yk}var tB,eB,rB,iB,nB,oB,sB,aB,hB,uB,fB=!1;function lB(t,e,r){var i,n=e.length,o=t.modulus.byteLength();if(n>o-11)throw new Error("message too long");return i=r?uB.alloc(o-n-3,255):function(t){var e,r=uB.allocUnsafe(t),i=0,n=rB(2*t),o=0;for(;ir-s-2)throw new Error("message too long");var a=uB.alloc(r-i-s-2),h=r-o-1,u=rB(o),f=oB(uB.concat([n,a,uB.alloc(1,1),e],h),nB(u,h)),l=oB(u,nB(f,o));return new sB(uB.concat([uB.alloc(1),l,f],r))}(o,e);else if(1===i)n=lB(o,e,r);else{if(3!==i)throw new Error("unknown padding");if((n=new sB(e)).cmp(o.modulus)>=0)throw new Error("data too long for modulus")}return r?hB(n,o):aB(n,o)}}var cB,pB,mB,gB,vB,bB,yB,wB,MB,_B=!1;function AB(t,e){var r=t.modulus.byteLength(),i=yB("sha1").update(MB.alloc(0)).digest(),n=i.length;if(0!==e[0])throw new Error("decryption error");var o=e.slice(1,n+1),s=e.slice(n+1),a=gB(o,mB(s,n)),h=gB(s,mB(a,r-n-1));if(function(t,e){t=MB.from(t),e=MB.from(e);var r=0,i=t.length;t.length!==e.length&&(r++,i=Math.min(t.length,e.length));var n=-1;for(;++ns||new vB(e).cmp(o.modulus)>=0)throw new Error("decryption error");n=r?wB(new vB(e),o):bB(e,o);var a=MB.alloc(s-n.length);if(n=MB.concat([a,n],s),4===i)return AB(o,n);if(1===i)return function(t,e,r){for(var i=e.slice(0,2),n=2,o=0;0!==e[n++];)if(n>=e.length){o++;break}var s=e.slice(2,n-1);if(("0002"!==i.toString("hex")&&!r||"0001"!==i.toString("hex")&&r)&&o++,s.length<8&&o++,o)throw new Error("decryption error");return e.slice(n)}(0,n,r);if(3===i)return n;throw new Error("unknown padding")}}var SB,kB,BB,RB,TB,xB=!1;function IB(){SB={},fB||(fB=!0,dB()),kB=tB,SB.publicEncrypt=kB,_B||(_B=!0,EB()),BB=cB,SB.privateDecrypt=BB,RB=function(t,e){return kB(t,e,!0)},SB.privateEncrypt=RB,TB=function(t,e){return BB(t,e,!0)},SB.publicDecrypt=TB}var CB,PB,DB,LB,OB,jB,UB,NB=!1;function zB(){throw new Error("secure random number generation not supported by this browser\nuse chrome, FireFox or Internet Explorer 11")}function qB(t,e){if("number"!=typeof t||t!=t)throw new TypeError("offset must be a number");if(t>UB||t<0)throw new TypeError("offset must be a uint32");if(t>OB||t>e)throw new RangeError("offset out of range")}function FB(t,e,r){if("number"!=typeof t||t!=t)throw new TypeError("size must be a number");if(t>UB||t<0)throw new TypeError("size must be a uint32");if(t+e>r||t>OB)throw new RangeError("buffer too small")}function ZB(e,r,i,n){if(!(LB.isBuffer(e)||e instanceof t.Uint8Array))throw new TypeError('"buf" argument must be a Buffer or Uint8Array');if("function"==typeof r)n=r,r=0,i=e.length;else if("function"==typeof i)n=i,i=e.length-r;else if("function"!=typeof n)throw new TypeError('"cb" argument must be a function');return qB(r,e.length),FB(i,r,e.length),HB(e,r,i,n)}function HB(t,e,r,i){var n=t.buffer,o=new Uint8Array(n,e,r);return jB.getRandomValues(o),i?void jt().nextTick((function(){i(null,t)})):t}function KB(e,r,i){if(void 0===r&&(r=0),!(LB.isBuffer(e)||e instanceof t.Uint8Array))throw new TypeError('"buf" argument must be a Buffer or Uint8Array');return qB(r,e.length),void 0===i&&(i=e.length-r),FB(i,r,e.length),HB(e,r,i)}function WB(){return NB||(NB=!0,DB={},jt(),lr(),wr(),LB=lr().Buffer,OB=lr().kMaxLength,jB=t.crypto||t.msCrypto,UB=Math.pow(2,32)-1,jB&&jB.getRandomValues?(CB=ZB,DB.randomFill=CB,PB=KB,DB.randomFillSync=PB):(CB=zB,DB.randomFill=CB,PB=zB,DB.randomFillSync=PB)),DB}var GB,YB,VB,$B,XB,JB,QB,tR,eR,rR,iR,nR,oR=!1;function sR(){JB={},VB=wr(),YB=JB.prng=VB,GB=JB.pseudoRandomBytes=YB,QB=JB.rng=GB,JB.randomBytes=QB,$B=uu(),tR=JB.Hash=$B,JB.createHash=tR,XB=Du(),eR=JB.Hmac=XB,JB.createHmac=eR,rR=zu(),iR=Object.keys(rR),["sha1","sha224","sha256","sha384","sha512","md5","rmd160"].concat(iR),Lf(),fm(),Bg||(Bg=!0,xg()),Ak(),Ik||(Ik=!0,Dk()),nR=Bk,JB.createECDH=nR,xB||(xB=!0,IB()),WB()}function aR(){return oR||(oR=!0,sR()),JB}var hR,uR,fR,lR,dR,cR,pR,mR,gR,vR,bR,yR,wR,MR,_R,AR,ER,SR,kR,BR,RR,TR,xR,IR,CR,PR,DR,LR,OR,jR,UR,NR,zR,qR,FR,ZR,HR,KR,WR,GR,YR,VR,$R,XR,JR,QR,tT,eT,rT,iT,nT,oT,sT,aT,hT,uT,fT,lT,dT,cT,pT,mT,gT,vT,bT,yT,wT,MT,_T,AT,ET,ST,kT,BT,RT,TT,xT,IT,CT,PT,DT,LT,OT,jT=!1;function UT(t){return lR.locateFile?lR.locateFile(t,wR):wR+t}function NT(t){NT.shown||(NT.shown={}),NT.shown[t]||(NT.shown[t]=1,BR(t))}function zT(t,e){t||tx("Assertion failed: "+e)}function qT(t,e,r){for(var i=e+r,n=e;t[n]&&!(n>=i);)++n;if(n-e>16&&t.subarray&&PR)return PR.decode(t.subarray(e,n));for(var o="";e>10,56320|1023&u)}}else o+=String.fromCharCode((31&s)<<6|a)}else o+=String.fromCharCode(s)}return o}function FT(t,e){return t?qT(jR,t,e):""}function ZT(t,e,r,i){if(!(i>0))return 0;for(var n=r,o=r+i-1,s=0;s=55296&&a<=57343)a=65536+((1023&a)<<10)|1023&t.charCodeAt(++s);if(a<=127){if(r>=o)break;e[r++]=a}else if(a<=2047){if(r+1>=o)break;e[r++]=192|a>>6,e[r++]=128|63&a}else if(a<=65535){if(r+2>=o)break;e[r++]=224|a>>12,e[r++]=128|a>>6&63,e[r++]=128|63&a}else{if(r+3>=o)break;e[r++]=240|a>>18,e[r++]=128|a>>12&63,e[r++]=128|a>>6&63,e[r++]=128|63&a}}return e[r]=0,r-n}function HT(t,e,r){return ZT(t,jR,e,r)}function KT(t){for(var e=0,r=0;r=55296&&i<=57343&&(i=65536+((1023&i)<<10)|1023&t.charCodeAt(++r)),i<=127?++e:e+=i<=2047?2:i<=65535?3:4}return e}function WT(t,e){for(var r=t,i=r>>1,n=i+e/2;!(i>=n)&&NR[i];)++i;if((r=i<<1)-t>32&&DR)return DR.decode(jR.subarray(t,r));for(var o="",s=0;!(s>=e/2);++s){var a=UR[t+2*s>>1];if(0==a)break;o+=String.fromCharCode(a)}return o}function GT(t,e,r){if(void 0===r&&(r=2147483647),r<2)return 0;for(var i=e,n=(r-=2)<2*t.length?r/2:t.length,o=0;o>1]=s,e+=2}return UR[e>>1]=0,e-i}function YT(t){return 2*t.length}function VT(t,e){for(var r=0,i="";!(r>=e/4);){var n=zR[t+4*r>>2];if(0==n)break;if(++r,n>=65536){var o=n-65536;i+=String.fromCharCode(55296|o>>10,56320|1023&o)}else i+=String.fromCharCode(n)}return i}function $T(t,e,r){if(void 0===r&&(r=2147483647),r<4)return 0;for(var i=e,n=i+r-4,o=0;o=55296&&s<=57343)s=65536+((1023&s)<<10)|1023&t.charCodeAt(++o);if(zR[e>>2]=s,(e+=4)+4>n)break}return zR[e>>2]=0,e-i}function XT(t){for(var e=0,r=0;r=55296&&i<=57343&&++r,e+=4}return e}function JT(t){YR++,lR.monitorRunDependencies&&lR.monitorRunDependencies(YR)}function QT(t){if(YR--,lR.monitorRunDependencies&&lR.monitorRunDependencies(YR),0==YR&&(null!==VR&&(clearInterval(VR),VR=null),$R)){var e=$R;$R=null,e()}}function tx(t){throw lR.onAbort&&lR.onAbort(t),BR(t+=""),CR=!0,1,t="abort("+t+"). Build with -s ASSERTIONS=1 for more info.",new WebAssembly.RuntimeError(t)}function ex(t){return t.startsWith(XR)}function rx(t){return t.startsWith("file://")}function ix(t){try{if(t==JR&&xR)return new Uint8Array(xR);if(AR)return AR(t);throw"both async and sync fetching of the wasm failed"}catch(t){tx(t)}}function nx(){var t={a:TT};function e(t,e){var r,i,n=t.exports;lR.asm=n,IR=lR.asm.J,r=IR.buffer,LR=r,lR.HEAP8=OR=new Int8Array(r),lR.HEAP16=UR=new Int16Array(r),lR.HEAP32=zR=new Int32Array(r),lR.HEAPU8=jR=new Uint8Array(r),lR.HEAPU16=NR=new Uint16Array(r),lR.HEAPU32=qR=new Uint32Array(r),lR.HEAPF32=FR=new Float32Array(r),lR.HEAPF64=ZR=new Float64Array(r),HR=lR.asm.N,i=lR.asm.K,WR.unshift(i),QT()}function r(t){e(t.instance)}function i(e){return function(){if(!xR&&(gR||vR)){if("function"==typeof fetch&&!rx(JR))return fetch(JR,{credentials:"same-origin"}).then((function(t){if(!t.ok)throw"failed to load wasm binary file at '"+JR+"'";return t.arrayBuffer()})).catch((function(){return ix(JR)}));if(_R)return new Promise((function(t,e){_R(JR,(function(e){t(new Uint8Array(e))}),e)}))}return Promise.resolve().then((function(){return ix(JR)}))}().then((function(e){return WebAssembly.instantiate(e,t)})).then(e,(function(t){BR("failed to asynchronously prepare wasm: "+t),tx(t)}))}if(JT(),lR.instantiateWasm)try{return lR.instantiateWasm(t,e)}catch(t){return BR("Module.instantiateWasm callback failed with error: "+t),!1}return xR||"function"!=typeof WebAssembly.instantiateStreaming||ex(JR)||rx(JR)||"function"!=typeof fetch?i(r):fetch(JR,{credentials:"same-origin"}).then((function(e){return WebAssembly.instantiateStreaming(e,t).then(r,(function(t){return BR("wasm streaming compile failed: "+t),BR("falling back to ArrayBuffer instantiation"),i(r)}))})),{}}function ox(t){for(;t.length>0;){var e=t.shift();if("function"!=typeof e){var r=e.func;"number"==typeof r?void 0===e.arg?HR.get(r)():HR.get(r)(e.arg):r(void 0===e.arg?null:e.arg)}else e(lR)}}function sx(){var t=new Error;if(!t.stack){try{throw new Error}catch(e){t=e}if(!t.stack)return"(no stack trace available)"}return t.stack.toString()}function ax(){if("object"==typeof crypto&&"function"==typeof crypto.getRandomValues){var t=new Uint8Array(1);return function(){return crypto.getRandomValues(t),t[0]}}if(bR)try{return aR(),function(){return aR().randomBytes(1)[0]}}catch(t){}return function(){tx("randomDevice")}}function hx(t){for(var e=function(t,e){return e||(e=RR),Math.ceil(t/e)*e}(t,65536),r=IT(e);t>1]=2,0;default:return-28;case 9:return o=28,zR[PT()>>2]=o,-1}}catch(t){return void 0!==oT&&t instanceof oT.ErrnoError||tx(t),-t.errno}var o}function fx(t,e,r){sT.varargs=r;try{var i=sT.getStr(t),n=r?sT.get():0;return oT.open(i,e,n).fd}catch(t){return void 0!==oT&&t instanceof oT.ErrnoError||tx(t),-t.errno}}function lx(t,e,r,i,n){}function dx(t){switch(t){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+t)}}function cx(){for(var t=new Array(256),e=0;e<256;++e)t[e]=String.fromCharCode(e);aT=t}function px(t){for(var e="",r=t;jR[r];)e+=aT[jR[r++]];return e}function mx(t){if(void 0===t)return"_unknown";var e=(t=t.replace(/[^a-zA-Z0-9_]/g,"$")).charCodeAt(0);return e>=lT&&e<=dT?"_"+t:t}function gx(t,e){return t=mx(t),new Function("body","return function "+t+'() {\n "use strict"; return body.apply(this, arguments);\n};\n')(e)}function vx(t,e){var r=gx(e,(function(t){this.name=e,this.message=t;var r=new Error(t).stack;void 0!==r&&(this.stack=this.toString()+"\n"+r.replace(/^Error(:[^\n]*)?\n/,""))}));return r.prototype=Object.create(t.prototype),r.prototype.constructor=r,r.prototype.toString=function(){return void 0===this.message?this.name:this.name+": "+this.message},r}function bx(t){throw new cT(t)}function yx(t){throw new pT(t)}function wx(t,e,r){function i(e){var i=r(e);i.length!==t.length&&yx("Mismatched type converter count");for(var n=0;n>o])},destructorFunction:null})}function Ax(t){if(!(this instanceof Dx))return!1;if(!(t instanceof Dx))return!1;for(var e=this.$$.ptrType.registeredClass,r=this.$$.ptr,i=t.$$.ptrType.registeredClass,n=t.$$.ptr;e.baseClass;)r=e.upcast(r),e=e.baseClass;for(;i.baseClass;)n=i.upcast(n),i=i.baseClass;return e===i&&r===n}function Ex(t){bx(t.$$.ptrType.registeredClass.name+" instance already deleted")}function Sx(t){}function kx(t){t.count.value-=1,0===t.count.value&&function(t){t.smartPtr?t.smartPtrType.rawDestructor(t.smartPtr):t.ptrType.registeredClass.rawDestructor(t.ptr)}(t)}function Bx(t){return"undefined"==typeof FinalizationGroup?(Bx=function(t){return t},t):(mT=new FinalizationGroup((function(t){for(var e=t.next();!e.done;e=t.next()){var r=e.value;r.ptr?kx(r):console.warn("object already deleted: "+r.ptr)}})),Bx=function(t){return mT.register(t,t.$$,t.$$),t},Sx=function(t){mT.unregister(t.$$)},Bx(t))}function Rx(){if(this.$$.ptr||Ex(this),this.$$.preservePointerOnDelete)return this.$$.count.value+=1,this;var t,e=Bx(Object.create(Object.getPrototypeOf(this),{$$:{value:(t=this.$$,{count:t.count,deleteScheduled:t.deleteScheduled,preservePointerOnDelete:t.preservePointerOnDelete,ptr:t.ptr,ptrType:t.ptrType,smartPtr:t.smartPtr,smartPtrType:t.smartPtrType})}}));return e.$$.count.value+=1,e.$$.deleteScheduled=!1,e}function Tx(){this.$$.ptr||Ex(this),this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete&&bx("Object already scheduled for deletion"),Sx(this),kx(this.$$),this.$$.preservePointerOnDelete||(this.$$.smartPtr=void 0,this.$$.ptr=void 0)}function xx(){return!this.$$.ptr}function Ix(){for(;vT.length;){var t=vT.pop();t.$$.deleteScheduled=!1,t.delete()}}function Cx(){return this.$$.ptr||Ex(this),this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete&&bx("Object already scheduled for deletion"),vT.push(this),1===vT.length&&gT&&gT(Ix),this.$$.deleteScheduled=!0,this}function Px(){Dx.prototype.isAliasOf=Ax,Dx.prototype.clone=Rx,Dx.prototype.delete=Tx,Dx.prototype.isDeleted=xx,Dx.prototype.deleteLater=Cx}function Dx(){}function Lx(t,e,r){if(void 0===t[e].overloadTable){var i=t[e];t[e]=function(){return t[e].overloadTable.hasOwnProperty(arguments.length)||bx("Function '"+r+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+t[e].overloadTable+")!"),t[e].overloadTable[arguments.length].apply(this,arguments)},t[e].overloadTable=[],t[e].overloadTable[i.argCount]=i}}function Ox(t,e,r,i,n,o,s,a){this.name=t,this.constructor=e,this.instancePrototype=r,this.rawDestructor=i,this.baseClass=n,this.getActualType=o,this.upcast=s,this.downcast=a,this.pureVirtualFunctions=[]}function jx(t,e,r){for(;e!==r;)e.upcast||bx("Expected null or instance of "+r.name+", got an instance of "+e.name),t=e.upcast(t),e=e.baseClass;return t}function Ux(t,e){if(null===e)return this.isReference&&bx("null is not a valid "+this.name),0;e.$$||bx('Cannot pass "'+yI(e)+'" as a '+this.name),e.$$.ptr||bx("Cannot pass deleted object as a pointer of type "+this.name);var r=e.$$.ptrType.registeredClass;return jx(e.$$.ptr,r,this.registeredClass)}function Nx(t,e){var r;if(null===e)return this.isReference&&bx("null is not a valid "+this.name),this.isSmartPointer?(r=this.rawConstructor(),null!==t&&t.push(this.rawDestructor,r),r):0;e.$$||bx('Cannot pass "'+yI(e)+'" as a '+this.name),e.$$.ptr||bx("Cannot pass deleted object as a pointer of type "+this.name),!this.isConst&&e.$$.ptrType.isConst&&bx("Cannot convert argument of type "+(e.$$.smartPtrType?e.$$.smartPtrType.name:e.$$.ptrType.name)+" to parameter type "+this.name);var i=e.$$.ptrType.registeredClass;if(r=jx(e.$$.ptr,i,this.registeredClass),this.isSmartPointer)switch(void 0===e.$$.smartPtr&&bx("Passing raw pointer to smart pointer is illegal"),this.sharingPolicy){case 0:e.$$.smartPtrType===this?r=e.$$.smartPtr:bx("Cannot convert argument of type "+(e.$$.smartPtrType?e.$$.smartPtrType.name:e.$$.ptrType.name)+" to parameter type "+this.name);break;case 1:r=e.$$.smartPtr;break;case 2:if(e.$$.smartPtrType===this)r=e.$$.smartPtr;else{var n=e.clone();r=this.rawShare(r,vI((function(){n.delete()}))),null!==t&&t.push(this.rawDestructor,r)}break;default:bx("Unsupporting sharing policy")}return r}function zx(t,e){if(null===e)return this.isReference&&bx("null is not a valid "+this.name),0;e.$$||bx('Cannot pass "'+yI(e)+'" as a '+this.name),e.$$.ptr||bx("Cannot pass deleted object as a pointer of type "+this.name),e.$$.ptrType.isConst&&bx("Cannot convert argument of type "+e.$$.ptrType.name+" to parameter type "+this.name);var r=e.$$.ptrType.registeredClass;return jx(e.$$.ptr,r,this.registeredClass)}function qx(t){return this.fromWireType(qR[t>>2])}function Fx(t){return this.rawGetPointee&&(t=this.rawGetPointee(t)),t}function Zx(t){this.rawDestructor&&this.rawDestructor(t)}function Hx(t){null!==t&&t.delete()}function Kx(t,e,r){if(e===r)return t;if(void 0===r.baseClass)return null;var i=Kx(t,e,r.baseClass);return null===i?null:r.downcast(i)}function Wx(){return Object.keys(yT).length}function Gx(){var t=[];for(var e in yT)yT.hasOwnProperty(e)&&t.push(yT[e]);return t}function Yx(t){gT=t,vT.length&&gT&&gT(Ix)}function Vx(){lR.getInheritedInstanceCount=Wx,lR.getLiveInheritedInstances=Gx,lR.flushPendingDeletes=Ix,lR.setDelayFunction=Yx}function $x(t,e){return e=function(t,e){for(void 0===e&&bx("ptr should not be undefined");t.baseClass;)e=t.upcast(e),t=t.baseClass;return e}(t,e),yT[e]}function Xx(t,e){return e.ptrType&&e.ptr||yx("makeClassHandle requires ptr and ptrType"),!!e.smartPtrType!==!!e.smartPtr&&yx("Both smartPtrType and smartPtr must be specified"),e.count={value:1},Bx(Object.create(t,{$$:{value:e}}))}function Jx(t){var e=this.getPointee(t);if(!e)return this.destructor(t),null;var r=$x(this.registeredClass,e);if(void 0!==r){if(0===r.$$.count.value)return r.$$.ptr=e,r.$$.smartPtr=t,r.clone();var i=r.clone();return this.destructor(t),i}function n(){return this.isSmartPointer?Xx(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:e,smartPtrType:this,smartPtr:t}):Xx(this.registeredClass.instancePrototype,{ptrType:this,ptr:t})}var o,s=this.registeredClass.getActualType(e),a=bT[s];if(!a)return n.call(this);o=this.isConst?a.constPointerType:a.pointerType;var h=Kx(e,this.registeredClass,o.registeredClass);return null===h?n.call(this):this.isSmartPointer?Xx(o.registeredClass.instancePrototype,{ptrType:o,ptr:h,smartPtrType:this,smartPtr:t}):Xx(o.registeredClass.instancePrototype,{ptrType:o,ptr:h})}function Qx(){tI.prototype.getPointee=Fx,tI.prototype.destructor=Zx,tI.prototype.argPackAdvance=8,tI.prototype.readValueFromPointer=qx,tI.prototype.deleteObject=Hx,tI.prototype.fromWireType=Jx}function tI(t,e,r,i,n,o,s,a,h,u,f){this.name=t,this.registeredClass=e,this.isReference=r,this.isConst=i,this.isSmartPointer=n,this.pointeeType=o,this.sharingPolicy=s,this.rawGetPointee=a,this.rawConstructor=h,this.rawShare=u,this.rawDestructor=f,n||void 0!==e.baseClass?this.toWireType=Nx:i?(this.toWireType=Ux,this.destructorFunction=null):(this.toWireType=zx,this.destructorFunction=null)}function eI(t,e,r){return t.includes("j")?function(t,e,r){var i=lR["dynCall_"+t];return r&&r.length?i.apply(null,[e].concat(r)):i.call(null,e)}(t,e,r):HR.get(e).apply(null,r)}function rI(t,e){var r,i,n,o=(t=px(t)).includes("j")?(r=t,i=e,n=[],function(){n.length=arguments.length;for(var t=0;t>2)+i]);return r}function aI(t){for(;t.length;){var e=t.pop();t.pop()(e)}}function hI(t,e,r,i,n,o){zT(e>0);var s=sI(e,r);n=rI(i,n);var a=[o],h=[];wx([],[t],(function(t){var r="constructor "+(t=t[0]).name;if(void 0===t.registeredClass.constructor_body&&(t.registeredClass.constructor_body=[]),void 0!==t.registeredClass.constructor_body[e-1])throw new cT("Cannot register multiple constructors with identical number of parameters ("+(e-1)+") for class '"+t.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!");return t.registeredClass.constructor_body[e-1]=function(){nI("Cannot construct "+t.name+" due to unbound types",s)},wx([],s,(function(i){return t.registeredClass.constructor_body[e-1]=function(){arguments.length!==e-1&&bx(r+" called with "+arguments.length+" arguments, expected "+(e-1)),h.length=0,a.length=e;for(var t=1;t0?", ":"")+l),d+=(u?"var rv = ":"")+"invoker(fn"+(l.length>0?", ":"")+l+");\n",a)d+="runDestructors(destructors);\n";else for(h=s?1:2;h4&&0==--_T[t].refcount&&(_T[t]=void 0,MT.push(t))}function pI(){for(var t=0,e=5;e<_T.length;++e)void 0!==_T[e]&&++t;return t}function mI(){for(var t=5;t<_T.length;++t)if(void 0!==_T[t])return _T[t];return null}function gI(){lR.count_emval_handles=pI,lR.get_first_emval=mI}function vI(t){switch(t){case void 0:return 1;case null:return 2;case!0:return 3;case!1:return 4;default:var e=MT.length?MT.pop():_T.length;return _T[e]={refcount:1,value:t},e}}function bI(t,e){Mx(t,{name:e=px(e),fromWireType:function(t){var e=_T[t].value;return cI(t),e},toWireType:function(t,e){return vI(e)},argPackAdvance:8,readValueFromPointer:qx,destructorFunction:null})}function yI(t){if(null===t)return"null";var e=typeof t;return"object"===e||"array"===e||"function"===e?t.toString():""+t}function wI(t,e){switch(e){case 2:return function(t){return this.fromWireType(FR[t>>2])};case 3:return function(t){return this.fromWireType(ZR[t>>3])};default:throw new TypeError("Unknown float type: "+t)}}function MI(t,e,r){var i=dx(r);Mx(t,{name:e=px(e),fromWireType:function(t){return t},toWireType:function(t,e){if("number"!=typeof e&&"boolean"!=typeof e)throw new TypeError('Cannot convert "'+yI(e)+'" to '+this.name);return e},argPackAdvance:8,readValueFromPointer:wI(e,i),destructorFunction:null})}function _I(t,e,r){switch(e){case 0:return r?function(t){return OR[t]}:function(t){return jR[t]};case 1:return r?function(t){return UR[t>>1]}:function(t){return NR[t>>1]};case 2:return r?function(t){return zR[t>>2]}:function(t){return qR[t>>2]};default:throw new TypeError("Unknown integer type: "+t)}}function AI(t,e,r,i,n){e=px(e),-1===n&&(n=4294967295);var o=dx(r),s=function(t){return t};if(0===i){var a=32-8*r;s=function(t){return t<>>a}}var h=e.includes("unsigned");Mx(t,{name:e,fromWireType:s,toWireType:function(t,r){if("number"!=typeof r&&"boolean"!=typeof r)throw new TypeError('Cannot convert "'+yI(r)+'" to '+this.name);if(rn)throw new TypeError('Passing a number "'+yI(r)+'" from JS side to C/C++ side to an argument of type "'+e+'", which is outside the valid range ['+i+", "+n+"]!");return h?r>>>0:0|r},argPackAdvance:8,readValueFromPointer:_I(e,o,0!==i),destructorFunction:null})}function EI(t,e,r){var i=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][e];function n(t){var e=qR,r=e[t>>=2],n=e[t+1];return new i(LR,n,r)}Mx(t,{name:r=px(r),fromWireType:n,argPackAdvance:8,readValueFromPointer:n},{ignoreDuplicateRegistrations:!0})}function SI(t,e){var r="std::string"===(e=px(e));Mx(t,{name:e,fromWireType:function(t){var e,i=qR[t>>2];if(r)for(var n=t+4,o=0;o<=i;++o){var s=t+4+o;if(o==i||0==jR[s]){var a=FT(n,s-n);void 0===e?e=a:(e+=String.fromCharCode(0),e+=a),n=s+1}}else{var h=new Array(i);for(o=0;o>2]=n,r&&i)HT(e,o+4,n+1);else if(i)for(var s=0;s255&&(xT(o),bx("String has UTF-16 code units that do not fit in 8 bits")),jR[o+4+s]=a}else for(s=0;s>2],s=o(),h=t+4,u=0;u<=n;++u){var f=t+4+u*e;if(u==n||0==s[f>>a]){var l=i(h,f-h);void 0===r?r=l:(r+=String.fromCharCode(0),r+=l),h=f+e}}return xT(t),r},toWireType:function(t,i){"string"!=typeof i&&bx("Cannot pass non-string to C++ string type "+r);var o=s(i),h=IT(4+o+e);return qR[h>>2]=o>>a,n(i,h+4,o+e),null!==t&&t.push(xT,h),h},argPackAdvance:8,readValueFromPointer:qx,destructorFunction:function(t){xT(t)}})}function BI(t,e){Mx(t,{isVoid:!0,name:e=px(e),argPackAdvance:0,fromWireType:function(){},toWireType:function(t,e){}})}function RI(t){return t||bx("Cannot use deleted val. handle = "+t),_T[t].value}function TI(t,e){var r=uT[t];return void 0===r&&bx(e+" has unknown type "+iI(t)),r}function xI(t,e,r){t=RI(t),e=TI(e,"emval::as");var i=[],n=vI(i);return zR[r>>2]=n,e.toWireType(i,t)}function II(t,e,r,i){var n,o;(t=ET[t])(e=RI(e),r=void 0===(o=AT[n=r])?px(n):o,null,i)}function CI(t,e){for(var r=function(t,e){for(var r=new Array(t),i=0;i>2)+i],"parameter "+i);return r}(t,e),i=r[0],n=i.name+"_$"+r.slice(1).map((function(t){return t.name})).join("_")+"$",o=["retType"],s=[i],a="",h=0;h4&&(_T[t].refcount+=1)}function DI(t){aI(_T[t].value),cI(t)}function LI(t,e){return vI((t=TI(t,"_emval_take_value")).readValueFromPointer(e))}function OI(){tx()}function jI(){return void 0===jI.start&&(jI.start=Date.now()),1e3*(Date.now()-jI.start)|0}function UI(t,e){return(t>>>0)+4294967296*e}function NI(t,e){if(t<=0)return t;var r=e<=32?Math.abs(1<=r&&(e<=32||t>r)&&(t=-2*r+t),t}function zI(t,e){return t>=0?t:e<=32?2*Math.abs(1<0?"\n":"")+function(t){var e=sx(),r=e.lastIndexOf("_emscripten_log"),i=e.lastIndexOf("_emscripten_get_callstack"),n=e.indexOf("\n",Math.max(r,i))+1;e=e.slice(n),32&t&&NT("EM_LOG_DEMANGLE is deprecated; ignoring"),8&t&&"undefined"==typeof emscripten_source_map&&(NT('Source map information is not available, emscripten_log with EM_LOG_C_STACK will be ignored. Build with "--pre-js $EMSCRIPTEN/src/emscripten-source-map.min.js" linker flag to add source map loading to code.'),t^=8,t|=16);var o=null;if(128&t)for(o=qI(arguments);o[1].includes("_emscripten_");)o=qI(o[0]);var s=e.split("\n");e="";var a=new RegExp("\\s*(.*?)@(.*?):([0-9]+):([0-9]+)"),h=new RegExp("\\s*(.*?)@(.*):(.*)(:(.*))?"),u=new RegExp("\\s*at (.*?) \\((.*):(.*):(.*)\\)");for(var f in s){var l=s[f],d="",c="",p=0,m=0,g=u.exec(l);if(g&&5==g.length)d=g[1],c=g[2],p=g[3],m=g[4];else{if((g=a.exec(l))||(g=h.exec(l)),!(g&&g.length>=4)){e+=l+"\n";continue}d=g[1],c=g[2],p=g[3],m=0|g[4]}var v=!1;if(8&t){var b=emscripten_source_map.originalPositionFor({line:p,column:m});(v=b&&b.source)&&(64&t&&(b.source=b.source.substring(b.source.replace(/\\/g,"/").lastIndexOf("/")+1)),e+=" at "+d+" ("+b.source+":"+b.line+":"+b.column+")\n")}(16&t||!v)&&(64&t&&(c=c.substring(c.replace(/\\/g,"/").lastIndexOf("/")+1)),e+=(v?" = "+d:" at "+d)+" ("+c+":"+p+":"+m+")\n"),128&t&&o[0]&&(o[1]==d&&o[2].length>0&&(e=e.replace(/\s+$/,""),e+=" with values: "+o[1]+o[2]+"\n"),o=qI(o[0]))}return e.replace(/\s+$/,"")}(t)),1&t?4&t?console.error(e):2&t?console.warn(e):512&t?console.info(e):256&t?console.debug(e):console.log(e):6&t?BR(e):kR(e)}function ZI(t,e,r){var i=function(t,e){var r=t,i=e;function n(t){var e;return i=function(t,e){return"double"!==e&&"i64"!==e||7&t&&(t+=4),t}(i,t),"double"===t?(e=ZR[i>>3],i+=8):"i64"==t?(e=[zR[i>>2],zR[i+4>>2]],i+=8):(t="i32",e=zR[i>>2],i+=4),e}for(var o,s,a,h,u=[];;){var f=r;if(0===(o=OR[r>>0]))break;if(s=OR[r+1>>0],37==o){var l=!1,d=!1,c=!1,p=!1,m=!1;t:for(;;){switch(s){case 43:l=!0;break;case 45:d=!0;break;case 35:c=!0;break;case 48:if(p)break t;p=!0;break;case 32:m=!0;break;default:break t}r++,s=OR[r+1>>0]}var g=0;if(42==s)g=n("i32"),r++,s=OR[r+1>>0];else for(;s>=48&&s<=57;)g=10*g+(s-48),r++,s=OR[r+1>>0];var v,b=!1,y=-1;if(46==s){if(y=0,b=!0,r++,42==(s=OR[r+1>>0]))y=n("i32"),r++;else for(;;){var w=OR[r+1>>0];if(w<48||w>57)break;y=10*y+(w-48),r++}s=OR[r+1>>0]}switch(y<0&&(y=6,b=!1),String.fromCharCode(s)){case"h":104==OR[r+2>>0]?(r++,v=1):v=2;break;case"l":108==OR[r+2>>0]?(r++,v=8):v=4;break;case"L":case"q":case"j":v=8;break;case"z":case"t":case"I":v=4;break;default:v=null}switch(v&&r++,s=OR[r+1>>0],String.fromCharCode(s)){case"d":case"i":case"u":case"o":case"x":case"X":case"p":var M=100==s||105==s;a=n("i"+8*(v=v||4)),8==v&&(a=117==s?(a[0]>>>0)+4294967296*(a[1]>>>0):UI(a[0],a[1])),v<=4&&(a=(M?NI:zI)(a&Math.pow(256,v)-1,8*v));var _=Math.abs(a),A="";if(100==s||105==s)k=NI(a,8*v).toString(10);else if(117==s)k=zI(a,8*v).toString(10),a=Math.abs(a);else if(111==s)k=(c?"0":"")+_.toString(8);else if(120==s||88==s){if(A=c&&0!=a?"0x":"",a<0){a=-a,k=(_-1).toString(16);for(var E=[],S=0;S=0&&(l?A="+"+A:m&&(A=" "+A)),"-"==k.charAt(0)&&(A="-"+A,k=k.substr(1));A.length+k.lengthT&&T>=-4?(s=(103==s?"f":"F").charCodeAt(0),y-=T+1):(s=(103==s?"e":"E").charCodeAt(0),y--),R=Math.min(y,20)}101==s||69==s?(k=a.toExponential(R),/[eE][-+]\d$/.test(k)&&(k=k.slice(0,-1)+"0"+k.slice(-1))):102!=s&&70!=s||(k=a.toFixed(R),0===a&&((h=a)<0||0===h&&1/h==-1/0)&&(k="-"+k));var x=k.split("e");if(B&&!c)for(;x[0].length>1&&x[0].includes(".")&&("0"==x[0].slice(-1)||"."==x[0].slice(-1));)x[0]=x[0].slice(0,-1);else for(c&&-1==k.indexOf(".")&&(x[0]+=".");y>R++;)x[0]+="0";k=x[0]+(x.length>1?"e"+x[1]:""),69==s&&(k=k.toUpperCase()),a>=0&&(l?k="+"+k:m&&(k=" "+k))}else k=(a<0?"-":"")+"inf",p=!1;for(;k.length>0]);else u=u.concat(eC("(null)".substr(0,C),!0));if(d)for(;C0;)u.push(32);d||u.push(n("i8"));break;case"n":var P=n("i32*");zR[P>>2]=u.length;break;case"%":u.push(o);break;default:for(S=f;S>0])}r+=2}else u.push(o),r+=1}return u}(e,r);FI(t,qT(i,0))}function HI(t){jR.length;tx("OOM")}function KI(){if(!KI.strings){var t={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:mR||"./this.program"};for(var e in ST)t[e]=ST[e];var r=[];for(var e in t)r.push(e+"="+t[e]);KI.strings=r}return KI.strings}function WI(t,e){try{var r=0;return KI().forEach((function(i,n){var o=e+r;zR[t+4*n>>2]=o,function(t,e,r){for(var i=0;i>0]=t.charCodeAt(i);r||(OR[e>>0]=0)}(i,o),r+=i.length+1})),0}catch(t){return void 0!==oT&&t instanceof oT.ErrnoError||tx(t),t.errno}}function GI(t,e){try{var r=KI();zR[t>>2]=r.length;var i=0;return r.forEach((function(t){i+=t.length+1})),zR[e>>2]=i,0}catch(t){return void 0!==oT&&t instanceof oT.ErrnoError||tx(t),t.errno}}function YI(t){try{var e=sT.getStreamFromFD(t);return oT.close(e),0}catch(t){return void 0!==oT&&t instanceof oT.ErrnoError||tx(t),t.errno}}function VI(t,e){try{var r=sT.getStreamFromFD(t),i=r.tty?2:oT.isDir(r.mode)?3:oT.isLink(r.mode)?7:4;return OR[e>>0]=i,0}catch(t){return void 0!==oT&&t instanceof oT.ErrnoError||tx(t),t.errno}}function $I(t,e,r,i){try{var n=sT.getStreamFromFD(t),o=sT.doReadv(n,e,r);return zR[i>>2]=o,0}catch(t){return void 0!==oT&&t instanceof oT.ErrnoError||tx(t),t.errno}}function XI(t,e,r,i,n){try{var o=sT.getStreamFromFD(t),s=4294967296*r+(e>>>0),a=9007199254740992;return s<=-a||s>=a?-61:(oT.llseek(o,s,i),tT=[o.position>>>0,(QR=o.position,+Math.abs(QR)>=1?QR>0?(0|Math.min(+Math.floor(QR/4294967296),4294967295))>>>0:~~+Math.ceil((QR-+(~~QR>>>0))/4294967296)>>>0:0)],zR[n>>2]=tT[0],zR[n+4>>2]=tT[1],o.getdents&&0===s&&0===i&&(o.getdents=null),0)}catch(t){return void 0!==oT&&t instanceof oT.ErrnoError||tx(t),t.errno}}function JI(t,e,r,i){try{var n=sT.getStreamFromFD(t),o=sT.doWritev(n,e,r);return zR[i>>2]=o,0}catch(t){return void 0!==oT&&t instanceof oT.ErrnoError||tx(t),t.errno}}function QI(t){var e=Date.now();return zR[t>>2]=e/1e3|0,zR[t+4>>2]=e%1e3*1e3|0,0}function tC(t){TR(t)}function eC(t,e,r){var i=r>0?r:KT(t)+1,n=new Array(i),o=ZT(t,n,0,n.length);return e&&(n.length=o),n}function rC(t){this.name="ExitStatus",this.message="Program terminated with exit("+t+")",this.status=t}function iC(t){function e(){LT||(LT=!0,lR.calledRun=!0,CR||(!0,lR.noFSInit||oT.init.initialized||oT.init(),iT.init(),ox(WR),lR.onRuntimeInitialized&&lR.onRuntimeInitialized(),function(){if(lR.postRun)for("function"==typeof lR.postRun&&(lR.postRun=[lR.postRun]);lR.postRun.length;)t=lR.postRun.shift(),GR.unshift(t);var t;ox(GR)}()))}t=t||pR,YR>0||(!function(){if(lR.preRun)for("function"==typeof lR.preRun&&(lR.preRun=[lR.preRun]);lR.preRun.length;)t=lR.preRun.shift(),KR.unshift(t);var t;ox(KR)}(),YR>0||(lR.setStatus?(lR.setStatus("Running..."),setTimeout((function(){setTimeout((function(){lR.setStatus("")}),1),e()}),1)):e()))}function nC(){for(cR in{},hR=jt(),uR="/home/lin/server/jessibuca/demo/public",fR=Ye().Buffer,dR={},lR=void 0!==lR?lR:{})lR.hasOwnProperty(cR)&&(dR[cR]=lR[cR]);for(cR in pR=[],mR="./this.program",function(t,e){throw e},gR=!1,vR=!1,bR=!1,yR=!1,gR="object"==typeof window,vR="function"==typeof importScripts,bR="object"==typeof hR&&"object"==typeof hR.versions&&"string"==typeof hR.versions.node,yR=!gR&&!bR&&!vR,wR="",bR?(wR=vR?tr().dirname(wR)+"/":uR+"/",MR=function(t,e){return ER||(ER=ir()),SR||(SR=tr()),t=SR.normalize(t),ER.readFileSync(t,e?null:"utf8")},AR=function(t){var e=MR(t,!0);return e.buffer||(e=new Uint8Array(e)),zT(e.buffer),e},hR.argv.length>1&&(mR=hR.argv[1].replace(/\\/g,"/")),pR=hR.argv.slice(2),lR,hR.on("uncaughtException",(function(t){if(!(t instanceof rC))throw t})),hR.on("unhandledRejection",tx),function(t){hR.exit(t)},lR.inspect=function(){return"[Emscripten Module object]"}):yR?("undefined"!=typeof read&&(MR=function(t){return read(t)}),AR=function(t){var e;return"function"==typeof readbuffer?new Uint8Array(readbuffer(t)):(zT("object"==typeof(e=read(t,"binary"))),e)},"undefined"!=typeof scriptArgs?pR=scriptArgs:void 0!==arguments&&(pR=arguments),"function"==typeof quit&&function(t){quit(t)},"undefined"!=typeof print&&("undefined"==typeof console&&(console={}),console.log=print,console.warn=console.error="undefined"!=typeof printErr?printErr:print)):(gR||vR)&&(vR?wR=self.location.href:"undefined"!=typeof document&&document.currentScript&&(wR=document.currentScript.src),wR=0!==wR.indexOf("blob:")?wR.substr(0,wR.lastIndexOf("/")+1):"",MR=function(t){var e=new XMLHttpRequest;return e.open("GET",t,!1),e.send(null),e.responseText},vR&&(AR=function(t){var e=new XMLHttpRequest;return e.open("GET",t,!1),e.responseType="arraybuffer",e.send(null),new Uint8Array(e.response)}),_R=function(t,e,r){var i=new XMLHttpRequest;i.open("GET",t,!0),i.responseType="arraybuffer",i.onload=function(){200==i.status||0==i.status&&i.response?e(i.response):r()},i.onerror=r,i.send(null)},function(t){document.title=t}),kR=lR.print||console.log.bind(console),BR=lR.printErr||console.warn.bind(console),dR)dR.hasOwnProperty(cR)&&(lR[cR]=dR[cR]);if(dR=null,lR.arguments&&(pR=lR.arguments),lR.thisProgram&&(mR=lR.thisProgram),lR.quit&&lR.quit,RR=16,0,TR=function(t){t},lR.wasmBinary&&(xR=lR.wasmBinary),lR.noExitRuntime||!0,"object"!=typeof WebAssembly&&tx("no native wasm support detected"),CR=!1,PR="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,DR="undefined"!=typeof TextDecoder?new TextDecoder("utf-16le"):void 0,lR.INITIAL_MEMORY||67108864,KR=[],WR=[],GR=[],!1,YR=0,VR=null,$R=null,lR.preloadedImages={},lR.preloadedAudios={},XR="data:application/octet-stream;base64,",ex(JR="ff.wasm")||(JR=UT(JR)),eT={splitPath:function(t){return/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(t).slice(1)},normalizeArray:function(t,e){for(var r=0,i=t.length-1;i>=0;i--){var n=t[i];"."===n?t.splice(i,1):".."===n?(t.splice(i,1),r++):r&&(t.splice(i,1),r--)}if(e)for(;r;r--)t.unshift("..");return t},normalize:function(t){var e="/"===t.charAt(0),r="/"===t.substr(-1);return(t=eT.normalizeArray(t.split("/").filter((function(t){return!!t})),!e).join("/"))||e||(t="."),t&&r&&(t+="/"),(e?"/":"")+t},dirname:function(t){var e=eT.splitPath(t),r=e[0],i=e[1];return r||i?(i&&(i=i.substr(0,i.length-1)),r+i):"."},basename:function(t){if("/"===t)return"/";var e=(t=(t=eT.normalize(t)).replace(/\/$/,"")).lastIndexOf("/");return-1===e?t:t.substr(e+1)},extname:function(t){return eT.splitPath(t)[3]},join:function(){var t=Array.prototype.slice.call(arguments,0);return eT.normalize(t.join("/"))},join2:function(t,e){return eT.normalize(t+"/"+e)}},rT={resolve:function(){for(var t="",e=!1,r=arguments.length-1;r>=-1&&!e;r--){var i=r>=0?arguments[r]:oT.cwd();if("string"!=typeof i)throw new TypeError("Arguments to path.resolve must be strings");if(!i)return"";t=i+"/"+t,e="/"===i.charAt(0)}return(e?"/":"")+(t=eT.normalizeArray(t.split("/").filter((function(t){return!!t})),!e).join("/"))||"."},relative:function(t,e){function r(t){for(var e=0;e=0&&""===t[r];r--);return e>r?[]:t.slice(e,r-e+1)}t=rT.resolve(t).substr(1),e=rT.resolve(e).substr(1);for(var i=r(t.split("/")),n=r(e.split("/")),o=Math.min(i.length,n.length),s=o,a=0;a0?r.slice(0,i).toString("utf-8"):null}else"undefined"!=typeof window&&"function"==typeof window.prompt?null!==(e=window.prompt("Input: "))&&(e+="\n"):"function"==typeof readline&&null!==(e=readline())&&(e+="\n");if(!e)return null;t.input=eC(e,!0)}return t.input.shift()},put_char:function(t,e){null===e||10===e?(kR(qT(t.output,0)),t.output=[]):0!=e&&t.output.push(e)},flush:function(t){t.output&&t.output.length>0&&(kR(qT(t.output,0)),t.output=[])}},default_tty1_ops:{put_char:function(t,e){null===e||10===e?(BR(qT(t.output,0)),t.output=[]):0!=e&&t.output.push(e)},flush:function(t){t.output&&t.output.length>0&&(BR(qT(t.output,0)),t.output=[])}}},nT={ops_table:null,mount:function(t){return nT.createNode(null,"/",16895,0)},createNode:function(t,e,r,i){if(oT.isBlkdev(r)||oT.isFIFO(r))throw new oT.ErrnoError(63);nT.ops_table||(nT.ops_table={dir:{node:{getattr:nT.node_ops.getattr,setattr:nT.node_ops.setattr,lookup:nT.node_ops.lookup,mknod:nT.node_ops.mknod,rename:nT.node_ops.rename,unlink:nT.node_ops.unlink,rmdir:nT.node_ops.rmdir,readdir:nT.node_ops.readdir,symlink:nT.node_ops.symlink},stream:{llseek:nT.stream_ops.llseek}},file:{node:{getattr:nT.node_ops.getattr,setattr:nT.node_ops.setattr},stream:{llseek:nT.stream_ops.llseek,read:nT.stream_ops.read,write:nT.stream_ops.write,allocate:nT.stream_ops.allocate,mmap:nT.stream_ops.mmap,msync:nT.stream_ops.msync}},link:{node:{getattr:nT.node_ops.getattr,setattr:nT.node_ops.setattr,readlink:nT.node_ops.readlink},stream:{}},chrdev:{node:{getattr:nT.node_ops.getattr,setattr:nT.node_ops.setattr},stream:oT.chrdev_stream_ops}});var n=oT.createNode(t,e,r,i);return oT.isDir(n.mode)?(n.node_ops=nT.ops_table.dir.node,n.stream_ops=nT.ops_table.dir.stream,n.contents={}):oT.isFile(n.mode)?(n.node_ops=nT.ops_table.file.node,n.stream_ops=nT.ops_table.file.stream,n.usedBytes=0,n.contents=null):oT.isLink(n.mode)?(n.node_ops=nT.ops_table.link.node,n.stream_ops=nT.ops_table.link.stream):oT.isChrdev(n.mode)&&(n.node_ops=nT.ops_table.chrdev.node,n.stream_ops=nT.ops_table.chrdev.stream),n.timestamp=Date.now(),t&&(t.contents[e]=n,t.timestamp=n.timestamp),n},getFileDataAsTypedArray:function(t){return t.contents?t.contents.subarray?t.contents.subarray(0,t.usedBytes):new Uint8Array(t.contents):new Uint8Array(0)},expandFileStorage:function(t,e){var r=t.contents?t.contents.length:0;if(!(r>=e)){e=Math.max(e,r*(r<1048576?2:1.125)>>>0),0!=r&&(e=Math.max(e,256));var i=t.contents;t.contents=new Uint8Array(e),t.usedBytes>0&&t.contents.set(i.subarray(0,t.usedBytes),0)}},resizeFileStorage:function(t,e){if(t.usedBytes!=e)if(0==e)t.contents=null,t.usedBytes=0;else{var r=t.contents;t.contents=new Uint8Array(e),r&&t.contents.set(r.subarray(0,Math.min(e,t.usedBytes))),t.usedBytes=e}},node_ops:{getattr:function(t){var e={};return e.dev=oT.isChrdev(t.mode)?t.id:1,e.ino=t.id,e.mode=t.mode,e.nlink=1,e.uid=0,e.gid=0,e.rdev=t.rdev,oT.isDir(t.mode)?e.size=4096:oT.isFile(t.mode)?e.size=t.usedBytes:oT.isLink(t.mode)?e.size=t.link.length:e.size=0,e.atime=new Date(t.timestamp),e.mtime=new Date(t.timestamp),e.ctime=new Date(t.timestamp),e.blksize=4096,e.blocks=Math.ceil(e.size/e.blksize),e},setattr:function(t,e){void 0!==e.mode&&(t.mode=e.mode),void 0!==e.timestamp&&(t.timestamp=e.timestamp),void 0!==e.size&&nT.resizeFileStorage(t,e.size)},lookup:function(t,e){throw oT.genericErrors[44]},mknod:function(t,e,r,i){return nT.createNode(t,e,r,i)},rename:function(t,e,r){if(oT.isDir(t.mode)){var i;try{i=oT.lookupNode(e,r)}catch(t){}if(i)for(var n in i.contents)throw new oT.ErrnoError(55)}delete t.parent.contents[t.name],t.parent.timestamp=Date.now(),t.name=r,e.contents[r]=t,e.timestamp=t.parent.timestamp,t.parent=e},unlink:function(t,e){delete t.contents[e],t.timestamp=Date.now()},rmdir:function(t,e){var r=oT.lookupNode(t,e);for(var i in r.contents)throw new oT.ErrnoError(55);delete t.contents[e],t.timestamp=Date.now()},readdir:function(t){var e=[".",".."];for(var r in t.contents)t.contents.hasOwnProperty(r)&&e.push(r);return e},symlink:function(t,e,r){var i=nT.createNode(t,e,41471,0);return i.link=r,i},readlink:function(t){if(!oT.isLink(t.mode))throw new oT.ErrnoError(28);return t.link}},stream_ops:{read:function(t,e,r,i,n){var o=t.node.contents;if(n>=t.node.usedBytes)return 0;var s=Math.min(t.node.usedBytes-n,i);if(s>8&&o.subarray)e.set(o.subarray(n,n+s),r);else for(var a=0;a0||i+r8)throw new oT.ErrnoError(32);for(var n=eT.normalizeArray(t.split("/").filter((function(t){return!!t})),!1),o=oT.root,s="/",a=0;a40)throw new oT.ErrnoError(32)}}return{path:s,node:o}},getPath:function(t){for(var e;;){if(oT.isRoot(t)){var r=t.mount.mountpoint;return e?"/"!==r[r.length-1]?r+"/"+e:r+e:r}e=e?t.name+"/"+e:t.name,t=t.parent}},hashName:function(t,e){for(var r=0,i=0;i>>0)%oT.nameTable.length},hashAddNode:function(t){var e=oT.hashName(t.parent.id,t.name);t.name_next=oT.nameTable[e],oT.nameTable[e]=t},hashRemoveNode:function(t){var e=oT.hashName(t.parent.id,t.name);if(oT.nameTable[e]===t)oT.nameTable[e]=t.name_next;else for(var r=oT.nameTable[e];r;){if(r.name_next===t){r.name_next=t.name_next;break}r=r.name_next}},lookupNode:function(t,e){var r=oT.mayLookup(t);if(r)throw new oT.ErrnoError(r,t);for(var i=oT.hashName(t.id,e),n=oT.nameTable[i];n;n=n.name_next){var o=n.name;if(n.parent.id===t.id&&o===e)return n}return oT.lookup(t,e)},createNode:function(t,e,r,i){var n=new oT.FSNode(t,e,r,i);return oT.hashAddNode(n),n},destroyNode:function(t){oT.hashRemoveNode(t)},isRoot:function(t){return t===t.parent},isMountpoint:function(t){return!!t.mounted},isFile:function(t){return 32768==(61440&t)},isDir:function(t){return 16384==(61440&t)},isLink:function(t){return 40960==(61440&t)},isChrdev:function(t){return 8192==(61440&t)},isBlkdev:function(t){return 24576==(61440&t)},isFIFO:function(t){return 4096==(61440&t)},isSocket:function(t){return 49152==(49152&t)},flagModes:{r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090},modeStringToFlags:function(t){var e=oT.flagModes[t];if(void 0===e)throw new Error("Unknown file open mode: "+t);return e},flagsToPermissionString:function(t){var e=["r","w","rw"][3&t];return 512&t&&(e+="w"),e},nodePermissions:function(t,e){return oT.ignorePermissions||(!e.includes("r")||292&t.mode)&&(!e.includes("w")||146&t.mode)&&(!e.includes("x")||73&t.mode)?0:2},mayLookup:function(t){var e=oT.nodePermissions(t,"x");return e||(t.node_ops.lookup?0:2)},mayCreate:function(t,e){try{oT.lookupNode(t,e);return 20}catch(t){}return oT.nodePermissions(t,"wx")},mayDelete:function(t,e,r){var i;try{i=oT.lookupNode(t,e)}catch(t){return t.errno}var n=oT.nodePermissions(t,"wx");if(n)return n;if(r){if(!oT.isDir(i.mode))return 54;if(oT.isRoot(i)||oT.getPath(i)===oT.cwd())return 10}else if(oT.isDir(i.mode))return 31;return 0},mayOpen:function(t,e){return t?oT.isLink(t.mode)?32:oT.isDir(t.mode)&&("r"!==oT.flagsToPermissionString(e)||512&e)?31:oT.nodePermissions(t,oT.flagsToPermissionString(e)):44},MAX_OPEN_FDS:4096,nextfd:function(t,e){t=t||0,e=e||oT.MAX_OPEN_FDS;for(var r=t;r<=e;r++)if(!oT.streams[r])return r;throw new oT.ErrnoError(33)},getStream:function(t){return oT.streams[t]},createStream:function(t,e,r){oT.FSStream||(oT.FSStream=function(){},oT.FSStream.prototype={object:{get:function(){return this.node},set:function(t){this.node=t}},isRead:{get:function(){return 1!=(2097155&this.flags)}},isWrite:{get:function(){return 0!=(2097155&this.flags)}},isAppend:{get:function(){return 1024&this.flags}}});var i=new oT.FSStream;for(var n in t)i[n]=t[n];t=i;var o=oT.nextfd(e,r);return t.fd=o,oT.streams[o]=t,t},closeStream:function(t){oT.streams[t]=null},chrdev_stream_ops:{open:function(t){var e=oT.getDevice(t.node.rdev);t.stream_ops=e.stream_ops,t.stream_ops.open&&t.stream_ops.open(t)},llseek:function(){throw new oT.ErrnoError(70)}},major:function(t){return t>>8},minor:function(t){return 255&t},makedev:function(t,e){return t<<8|e},registerDevice:function(t,e){oT.devices[t]={stream_ops:e}},getDevice:function(t){return oT.devices[t]},getMounts:function(t){for(var e=[],r=[t];r.length;){var i=r.pop();e.push(i),r.push.apply(r,i.mounts)}return e},syncfs:function(t,e){"function"==typeof t&&(e=t,t=!1),oT.syncFSRequests++,oT.syncFSRequests>1&&BR("warning: "+oT.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work");var r=oT.getMounts(oT.root.mount),i=0;function n(t){return oT.syncFSRequests--,e(t)}function o(t){if(t)return o.errored?void 0:(o.errored=!0,n(t));++i>=r.length&&n(null)}r.forEach((function(e){if(!e.type.syncfs)return o(null);e.type.syncfs(e,t,o)}))},mount:function(t,e,r){var i,n="/"===r,o=!r;if(n&&oT.root)throw new oT.ErrnoError(10);if(!n&&!o){var s=oT.lookupPath(r,{follow_mount:!1});if(r=s.path,i=s.node,oT.isMountpoint(i))throw new oT.ErrnoError(10);if(!oT.isDir(i.mode))throw new oT.ErrnoError(54)}var a={type:t,opts:e,mountpoint:r,mounts:[]},h=t.mount(a);return h.mount=a,a.root=h,n?oT.root=h:i&&(i.mounted=a,i.mount&&i.mount.mounts.push(a)),h},unmount:function(t){var e=oT.lookupPath(t,{follow_mount:!1});if(!oT.isMountpoint(e.node))throw new oT.ErrnoError(28);var r=e.node,i=r.mounted,n=oT.getMounts(i);Object.keys(oT.nameTable).forEach((function(t){for(var e=oT.nameTable[t];e;){var r=e.name_next;n.includes(e.mount)&&oT.destroyNode(e),e=r}})),r.mounted=null;var o=r.mount.mounts.indexOf(i);r.mount.mounts.splice(o,1)},lookup:function(t,e){return t.node_ops.lookup(t,e)},mknod:function(t,e,r){var i=oT.lookupPath(t,{parent:!0}).node,n=eT.basename(t);if(!n||"."===n||".."===n)throw new oT.ErrnoError(28);var o=oT.mayCreate(i,n);if(o)throw new oT.ErrnoError(o);if(!i.node_ops.mknod)throw new oT.ErrnoError(63);return i.node_ops.mknod(i,n,e,r)},create:function(t,e){return e=void 0!==e?e:438,e&=4095,e|=32768,oT.mknod(t,e,0)},mkdir:function(t,e){return e=void 0!==e?e:511,e&=1023,e|=16384,oT.mknod(t,e,0)},mkdirTree:function(t,e){for(var r=t.split("/"),i="",n=0;nthis.length-1||t<0)){var e=t%this.chunkSize,r=t/this.chunkSize|0;return this.getter(r)[e]}},o.prototype.setDataGetter=function(t){this.getter=t},o.prototype.cacheLength=function(){var t=new XMLHttpRequest;if(t.open("HEAD",r,!1),t.send(null),!(t.status>=200&&t.status<300||304===t.status))throw new Error("Couldn't load "+r+". Status: "+t.status);var e,i=Number(t.getResponseHeader("Content-length")),n=(e=t.getResponseHeader("Accept-Ranges"))&&"bytes"===e,o=(e=t.getResponseHeader("Content-Encoding"))&&"gzip"===e,s=1048576;n||(s=i);var a=this;a.setDataGetter((function(t){var e=t*s,n=(t+1)*s-1;if(n=Math.min(n,i-1),void 0===a.chunks[t]&&(a.chunks[t]=function(t,e){if(t>e)throw new Error("invalid range ("+t+", "+e+") or no bytes requested!");if(e>i-1)throw new Error("only "+i+" bytes available! programmer error!");var n=new XMLHttpRequest;if(n.open("GET",r,!1),i!==s&&n.setRequestHeader("Range","bytes="+t+"-"+e),"undefined"!=typeof Uint8Array&&(n.responseType="arraybuffer"),n.overrideMimeType&&n.overrideMimeType("text/plain; charset=x-user-defined"),n.send(null),!(n.status>=200&&n.status<300||304===n.status))throw new Error("Couldn't load "+r+". Status: "+n.status);return void 0!==n.response?new Uint8Array(n.response||[]):eC(n.responseText||"",!0)}(e,n)),void 0===a.chunks[t])throw new Error("doXHR failed!");return a.chunks[t]})),!o&&i||(s=i=1,i=this.getter(0).length,s=i,kR("LazyFiles on gzip forces download of the whole file when length is accessed")),this._length=i,this._chunkSize=s,this.lengthKnown=!0},"undefined"!=typeof XMLHttpRequest){if(!vR)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var s=new o;Object.defineProperties(s,{length:{get:function(){return this.lengthKnown||this.cacheLength(),this._length}},chunkSize:{get:function(){return this.lengthKnown||this.cacheLength(),this._chunkSize}}});var a={isDevice:!1,contents:s}}else a={isDevice:!1,url:r};var h=oT.createFile(t,e,a,i,n);a.contents?h.contents=a.contents:a.url&&(h.contents=null,h.url=a.url),Object.defineProperties(h,{usedBytes:{get:function(){return this.contents.length}}});var u={};return Object.keys(h.stream_ops).forEach((function(t){var e=h.stream_ops[t];u[t]=function(){return oT.forceLoadFile(h),e.apply(null,arguments)}})),u.read=function(t,e,r,i,n){oT.forceLoadFile(h);var o=t.node.contents;if(n>=o.length)return 0;var s=Math.min(o.length-n,i);if(o.slice)for(var a=0;a>2]=i.dev,zR[r+4>>2]=0,zR[r+8>>2]=i.ino,zR[r+12>>2]=i.mode,zR[r+16>>2]=i.nlink,zR[r+20>>2]=i.uid,zR[r+24>>2]=i.gid,zR[r+28>>2]=i.rdev,zR[r+32>>2]=0,tT=[i.size>>>0,(QR=i.size,+Math.abs(QR)>=1?QR>0?(0|Math.min(+Math.floor(QR/4294967296),4294967295))>>>0:~~+Math.ceil((QR-+(~~QR>>>0))/4294967296)>>>0:0)],zR[r+40>>2]=tT[0],zR[r+44>>2]=tT[1],zR[r+48>>2]=4096,zR[r+52>>2]=i.blocks,zR[r+56>>2]=i.atime.getTime()/1e3|0,zR[r+60>>2]=0,zR[r+64>>2]=i.mtime.getTime()/1e3|0,zR[r+68>>2]=0,zR[r+72>>2]=i.ctime.getTime()/1e3|0,zR[r+76>>2]=0,tT=[i.ino>>>0,(QR=i.ino,+Math.abs(QR)>=1?QR>0?(0|Math.min(+Math.floor(QR/4294967296),4294967295))>>>0:~~+Math.ceil((QR-+(~~QR>>>0))/4294967296)>>>0:0)],zR[r+80>>2]=tT[0],zR[r+84>>2]=tT[1],0},doMsync:function(t,e,r,i,n){var o=jR.slice(t,t+r);oT.msync(e,o,n,r,i)},doMkdir:function(t,e){return"/"===(t=eT.normalize(t))[t.length-1]&&(t=t.substr(0,t.length-1)),oT.mkdir(t,e,0),0},doMknod:function(t,e,r){switch(61440&e){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}return oT.mknod(t,e,r),0},doReadlink:function(t,e,r){if(r<=0)return-28;var i=oT.readlink(t),n=Math.min(r,KT(i)),o=OR[e+n];return HT(i,e,r+1),OR[e+n]=o,n},doAccess:function(t,e){if(-8&e)return-28;var r;if(!(r=oT.lookupPath(t,{follow:!0}).node))return-44;var i="";return 4&e&&(i+="r"),2&e&&(i+="w"),1&e&&(i+="x"),i&&oT.nodePermissions(r,i)?-2:0},doDup:function(t,e,r){var i=oT.getStream(r);return i&&oT.close(i),oT.open(t,e,0,r,r).fd},doReadv:function(t,e,r,i){for(var n=0,o=0;o>2],a=zR[e+(8*o+4)>>2],h=oT.read(t,OR,s,a,i);if(h<0)return-1;if(n+=h,h>2],a=zR[e+(8*o+4)>>2],h=oT.write(t,OR,s,a,i);if(h<0)return-1;n+=h}return n},varargs:void 0,get:function(){return sT.varargs+=4,zR[sT.varargs-4>>2]},getStr:function(t){return FT(t)},getStreamFromFD:function(t){var e=oT.getStream(t);if(!e)throw new oT.ErrnoError(8);return e},get64:function(t,e){return t}},aT=void 0,hT={},uT={},fT={},lT=48,dT=57,cT=void 0,pT=void 0,mT=!1,gT=void 0,vT=[],bT={},yT={},wT=void 0,MT=[],_T=[{},{value:void 0},{value:null},{value:!0},{value:!1}],AT={},ET=[],ST={},kT=function(t,e,r,i){t||(t=this),this.parent=t,this.mount=t.mount,this.mounted=null,this.id=oT.nextInode++,this.name=e,this.mode=r,this.node_ops={},this.stream_ops={},this.rdev=i},BT=365,RT=146,Object.defineProperties(kT.prototype,{read:{get:function(){return(this.mode&BT)===BT},set:function(t){t?this.mode|=BT:this.mode&=~BT}},write:{get:function(){return(this.mode&RT)===RT},set:function(t){t?this.mode|=RT:this.mode&=~RT}},isFolder:{get:function(){return oT.isDir(this.mode)}},isDevice:{get:function(){return oT.isChrdev(this.mode)}}}),oT.FSNode=kT,oT.staticInit(),cx(),cT=lR.BindingError=vx(Error,"BindingError"),pT=lR.InternalError=vx(Error,"InternalError"),Px(),Qx(),Vx(),wT=lR.UnboundTypeError=vx(Error,"UnboundTypeError"),gI(),TT={z:ux,y:fx,t:lx,D:_x,o:oI,j:hI,g:fI,I:dI,C:bI,m:MI,c:AI,b:EI,n:SI,i:kI,E:BI,r:xI,e:II,p:cI,d:CI,H:PI,q:DI,w:LI,a:OI,G:jI,f:ZI,u:HI,v:WI,x:GI,l:YI,A:VI,B:$I,s:XI,k:JI,F:QI,h:tC},nx(),lR.___wasm_call_ctors=function(){return(lR.___wasm_call_ctors=lR.asm.K).apply(null,arguments)},xT=lR._free=function(){return(xT=lR._free=lR.asm.L).apply(null,arguments)},IT=lR._malloc=function(){return(IT=lR._malloc=lR.asm.M).apply(null,arguments)},CT=lR._strlen=function(){return(CT=lR._strlen=lR.asm.O).apply(null,arguments)},PT=lR.___errno_location=function(){return(PT=lR.___errno_location=lR.asm.P).apply(null,arguments)},DT=lR.___getTypeName=function(){return(DT=lR.___getTypeName=lR.asm.Q).apply(null,arguments)},lR.___embind_register_native_and_builtin_types=function(){return(lR.___embind_register_native_and_builtin_types=lR.asm.R).apply(null,arguments)},lR.dynCall_ijiii=function(){return(lR.dynCall_ijiii=lR.asm.S).apply(null,arguments)},lR.dynCall_viiijj=function(){return(lR.dynCall_viiijj=lR.asm.T).apply(null,arguments)},lR.dynCall_jij=function(){return(lR.dynCall_jij=lR.asm.U).apply(null,arguments)},lR.dynCall_jii=function(){return(lR.dynCall_jii=lR.asm.V).apply(null,arguments)},lR.dynCall_jiji=function(){return(lR.dynCall_jiji=lR.asm.W).apply(null,arguments)},lR._ff_h264_cabac_tables=77157,$R=function t(){LT||iC(),LT||($R=t)},lR.run=iC,lR.preInit)for("function"==typeof lR.preInit&&(lR.preInit=[lR.preInit]);lR.preInit.length>0;)lR.preInit.pop()();iC(),OT=e(lR)}var oC=!1;function sC(t){let e=t.next(),r=null;return i=>{var n=new Uint8Array(i);if(r){var o=new Uint8Array(r.length+n.length);o.set(r),o.set(n,r.length),n=o,r=null}for(;n.length>=e.value;){var s=n.slice(e.value);e=t.next(n.slice(0,e.value)),n=s}n.length>0&&(r=n)}}function aC(){({}),jT||(jT=!0,nC()),s(),(()=>{try{if("object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate){const t=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));if(t instanceof WebAssembly.Module)return new WebAssembly.Instance(t)instanceof WebAssembly.Instance}}catch(t){}return!1})(),Date.now||(Date.now=function(){return(new Date).getTime()}),OT.print=function(t){postMessage({cmd:"print",text:t})},OT.printErr=function(t){postMessage({cmd:"printErr",text:t})},OT.postRun=function(){var t=[],e={_firstCheckpoint:0,_lastCheckpoint:0,_intervalBytes:0,_lastSecondBytes:0,addBytes:function(t){0===e._firstCheckpoint?(e._firstCheckpoint=Date.now(),e._lastCheckpoint=e._firstCheckpoint,e._intervalBytes+=t):Date.now()-e._lastCheckpoint<1e3?e._intervalBytes+=t:(e._lastSecondBytes=e._intervalBytes,e._intervalBytes=t,e._lastCheckpoint=Date.now())},reset:function(){e._firstCheckpoint=e._lastCheckpoint=0,e._intervalBytes=0,e._lastSecondBytes=0},getCurrentKBps:function(){e.addBytes(0);var t=(Date.now()-e._lastCheckpoint)/1e3;return 0==t&&(t=1),e._intervalBytes/t/1024},getLastSecondKBps:function(){return e.addBytes(0),0!==e._lastSecondBytes?e._lastSecondBytes/1024:Date.now()-e._lastCheckpoint>=500?e.getCurrentKBps():0}},r={opt:{},initAudioPlanar:function(t,e){postMessage({cmd:"initAudioPlanar",samplerate:e,channels:t});var r=[],i=[],n=0;this.playAudioPlanar=function(e,o){for(var s=o,a=[],h=0,u=0;u<2;u++){var f=OT.HEAPU32[(e>>2)+u]>>2;a[u]=OT.HEAPF32.subarray(f,f+s)}if(n){if(!(s>=(o=1024-n)))return n+=s,r[0]=Float32Array.of(...r[0],...a[0]),void(2==t&&(r[1]=Float32Array.of(...r[1],...a[1])));i[0]=Float32Array.of(...r[0],...a[0].subarray(0,o)),2==t&&(i[1]=Float32Array.of(...r[1],...a[1].subarray(0,o))),postMessage({cmd:"playAudio",buffer:i},i.map((t=>t.buffer))),h=o,s-=o}for(n=s;n>=1024;n-=1024)i[0]=a[0].slice(h,h+=1024),2==t&&(i[1]=a[1].slice(h-1024,h)),postMessage({cmd:"playAudio",buffer:i},i.map((t=>t.buffer)));n&&(r[0]=a[0].slice(h),2==t&&(r[1]=a[1].slice(h)))}},inputFlv:function*(){yield 9;for(var e=new ArrayBuffer(4),r=new Uint8Array(e),o=new Uint32Array(e);;){r[3]=0;var s=yield 15,a=s[4];r[0]=s[7],r[1]=s[6],r[2]=s[5];var h=o[0];r[0]=s[10],r[1]=s[9],r[2]=s[8];var u=o[0];16777215===u&&(r[3]=s[11],u=o[0]);var f=yield h;switch(a){case 8:this.opt.hasAudio&&t.push({ts:u,payload:f,decoder:i,type:0});break;case 9:t.push({ts:u,payload:f,decoder:n,type:f[0]>>4})}}},play:function(r){this.opt.debug&&console.log("Jessibuca play",r),this.getDelay=function(t){return t?(this.firstTimestamp=t,this.startTimestamp=Date.now(),this.getDelay=function(t){return this.delay=Date.now()-this.startTimestamp-(t-this.firstTimestamp),this.delay},-1):-1};if(this.stopId=setInterval((()=>{if(t.length)if(this.dropping){for(e=t.shift();1!==e.type&&t.length;)e=t.shift();1===e.type&&(this.dropping=!1,e.decoder.decode(e.payload))}else{var e=t[0];if(-1===this.getDelay(e.ts))t.shift(),this.ts=e.ts,e.decoder.decode(e.payload);else if(this.delay>this.videoBuffer+1e3)this.dropping=!0;else for(;t.length&&(e=t[0],this.getDelay(e.ts)>this.videoBuffer);)t.shift(),this.ts=e.ts,e.decoder.decode(e.payload)}}),10),this.speedSamplerId=setInterval((()=>{postMessage({cmd:"kBps",kBps:e.getLastSecondKBps()})}),1e3),0==r.indexOf("http")){this.flvMode=!0;var o=this,a=new AbortController;fetch(r,{signal:a.signal}).then((function(t){var r=t.body.getReader(),i=o.inputFlv(),n=sC(i),s=function(){r.read().then((({done:t,value:r})=>{t?i.return(null):(e.addBytes(r.byteLength),n(r),s())})).catch((function(t){i.return(null),o.opt.debug&&console.error(t),-1===t.toString().indexOf("The user aborted a request")&&postMessage({cmd:"printErr",text:t.toString()})}))};s()})).catch((t=>{postMessage({cmd:"printErr",text:t.message})})),this._close=function(){a.abort()}}else{if(this.flvMode=-1!=r.indexOf(".flv")||this.opt.isFlv,this.ws=new WebSocket(r),this.ws.binaryType="arraybuffer",this.flvMode){let t=this.inputFlv();var h=sC(t);this.ws.onmessage=t=>{e.addBytes(t.data.byteLength),h(t.data)},this.ws.onerror=e=>{t.return(null),postMessage({cmd:"printErr",text:e.toString()})}}else this.ws.onmessage=r=>{e.addBytes(r.data.byteLength);var o=new DataView(r.data);switch(o.getUint8(0)){case 1:this.opt.hasAudio&&t.push({ts:o.getUint32(1,!1),payload:new Uint8Array(r.data,5),decoder:i,type:0});break;case 2:t.push({ts:o.getUint32(1,!1),payload:new Uint8Array(r.data,5),decoder:n,type:o.getUint8(5)>>4})}},this.ws.onerror=t=>{postMessage({cmd:"printErr",text:t.toString()})};this._close=function(){this.ws&&(this.ws.close&&this.ws.close(),this.ws=null)}}this.setVideoSize=function(t,e){postMessage({cmd:"initSize",w:t,h:e});var r=t*e,i=r>>2;if(this.opt.forceNoOffscreen||"undefined"==typeof OffscreenCanvas)this.draw=function(t,e,n,o){var s=[OT.HEAPU8.subarray(e,e+r),OT.HEAPU8.subarray(n,n+i),OT.HEAPU8.subarray(o,o+i)].map((t=>Uint8Array.from(t)));postMessage({cmd:"render",compositionTime:t,delay:this.delay,ts:this.ts,output:s},s.map((t=>t.buffer)))};else{this.offscreenCanvas=new OffscreenCanvas(t,e),this.offscreenCanvasGL=this.offscreenCanvas.getContext("webgl");var n=s().default(this.offscreenCanvasGL);this.draw=function(o,s,a,h){n(t,e,OT.HEAPU8.subarray(s,s+r),OT.HEAPU8.subarray(a,a+i),OT.HEAPU8.subarray(h,h+i));let u=this.offscreenCanvas.transferToImageBitmap();postMessage({cmd:"render",compositionTime:o,delay:this.delay,ts:this.ts,buffer:u},[u])}}}},close:function(){this._close&&(this.opt.debug&&console.log("worker close"),this._close(),clearInterval(this.stopId),this.stopId=null,clearInterval(this.speedSamplerId),this.speedSamplerId=null,e.reset(),this.ws=null,i.clear(),n.clear(),this.firstTimestamp=0,this.startTimestamp=0,this.delay=0,this.ts=0,this.flvMode=!1,this.offscreenCanvas&&(this.offscreenCanvas=null),this.offscreenCanvasGL&&(this.offscreenCanvasGL=null),t=[],delete this.playAudioPlanar,delete this.draw,delete this.getDelay)}},i=new OT.AudioDecoder(r),n=new OT.VideoDecoder(r);postMessage({cmd:"init"}),self.onmessage=function(t){var e=t.data;switch(e.cmd){case"init":r.opt=JSON.parse(e.opt),i.sample_rate=e.sampleRate;break;case"getProp":postMessage({cmd:"getProp",value:r[e.prop]});break;case"setProp":r[e.prop]=e.value;break;case"play":r.play(e.url);break;case"setVideoBuffer":r.videoBuffer=1e3*e.time|0;break;case"close":r.close()}}}}"undefined"==typeof importScripts?wt||(wt=!0,{},l(),z(),Y(),J(),bt(),w(),O(),yt=class{constructor(t){if(this._opt=Object.assign(w().DEFAULT_OPTIONS,t),this.$container=t.container,"string"==typeof t.container&&(this.$container=document.querySelector(t.container)),!this.$container)throw new Error("Jessibuca need container option");delete this._opt.container,this._opt.debug&&console.log("options",this._opt),bt().default(this),l().default(this),z().default(this),J().default(this),Y().default(this)}set fullscreen(t){t?(O().checkFull()||this.$container.requestFullscreen(),O().$domToggle(this.$doms.minScreenDom,!0),O().$domToggle(this.$doms.fullscreenDom,!1)):(O().checkFull()&&document.exitFullscreen(),O().$domToggle(this.$doms.minScreenDom,!1),O().$domToggle(this.$doms.fullscreenDom,!0)),this._fullscreen!==t&&(this.onFullscreen(t),this._trigger(w().EVEMTS.fullscreen,t)),this._fullscreen=t}get fullscreen(){return this._fullscreen}set playing(t){t?(O().$domToggle(this.$doms.playBigDom,!1),O().$domToggle(this.$doms.playDom,!1),O().$domToggle(this.$doms.pauseDom,!0),O().$domToggle(this.$doms.screenshotsDom,!0),O().$domToggle(this.$doms.recordDom,!0),this._quieting?(O().$domToggle(this.$doms.quietAudioDom,!0),O().$domToggle(this.$doms.playAudioDom,!1)):(O().$domToggle(this.$doms.quietAudioDom,!1),O().$domToggle(this.$doms.playAudioDom,!0))):(this.$doms.speedDom&&(this.$doms.speedDom.innerText=""),this._playUrl&&(O().$domToggle(this.$doms.playDom,!0),O().$domToggle(this.$doms.playBigDom,!0),O().$domToggle(this.$doms.pauseDom,!1)),O().$domToggle(this.$doms.recordDom,!1),O().$domToggle(this.$doms.recordingDom,!1),O().$domToggle(this.$doms.screenshotsDom,!1),O().$domToggle(this.$doms.quietAudioDom,!1),O().$domToggle(this.$doms.playAudioDom,!1)),this._playing!==t&&(t?(this.onPlay(),this._trigger(w().EVEMTS.play)):(this.onPause(),this._trigger(w().EVEMTS.pause))),this._playing=t}get playing(){return this._playing}set quieting(t){t?(O().$domToggle(this.$doms.quietAudioDom,!0),O().$domToggle(this.$doms.playAudioDom,!1)):(O().$domToggle(this.$doms.quietAudioDom,!1),O().$domToggle(this.$doms.playAudioDom,!0)),this._quieting!==t&&(this.onMute(t),this._trigger(w().EVEMTS.mute,t)),this._quieting=t}get quieting(){return this._quieting}set loading(t){t?(O().$hideBtns(this.$doms),O().$domToggle(this.$doms.fullscreenDom,!0),O().$domToggle(this.$doms.pauseDom,!0),O().$domToggle(this.$doms.loadingDom,!0)):O().$initBtns(this.$doms),this._loading=t}get loading(){return this._loading}set recording(t){t?(O().$domToggle(this.$doms.recordDom,!1),O().$domToggle(this.$doms.recordingDom,!0)):(O().$domToggle(this.$doms.recordDom,!0),O().$domToggle(this.$doms.recordingDom,!1)),this._recording!==t&&(this.onRecord(t),this._trigger(w().EVEMTS.record,t),this._recording=t)}get recording(){return this._recording}isPlaying(){return this.playing}isMute(){return this.quieting}setDebug(t){this._opt.isDebug=!!t}setTimeout(t){this._opt.timeout=Number(t)}setVod(t){this._opt.vod=!!t}setNoOffscreen(t){this._opt.forceNoOffscreen=!!t}setScaleMode(t){0===(t=Number(t))?(this._opt.isFullResize=!1,this._opt.isResize=!1):1===t?(this._opt.isFullResize=!1,this._opt.isResize=!0):2===t&&(this._opt.isFullResize=!0),this._resize()}mute(){this._mute()}cancelMute(){this._cancelMute()}audioResume(){this._cancelMute()}pause(){this._pause()}play(t){this._play(t)}close(){this._close()}destroy(){this._close(),this._destroyAudioContext(),this._destroyContextGL(),this._decoderWorker.terminate(),this._removeEventListener(),this._initCheckVariable(),this._off(),this._removeContainerChild()}clearView(){this._clearView()}resize(){this._resize()}setBufferTime(t){t=Number(t),this._decoderWorker.postMessage({cmd:w().POST_MESSAGE.setVideoBuffer,time:t})}setRotate(t){t=parseInt(t,10),this._opt.rotate!==t&&-1!==[0,90,270].indexOf(t)&&(this._opt.rotate=t,this.resize())}setVolume(t){if(this._gainNode){if(t=parseFloat(t),isNaN(t))return;this._gainNode.gain.setValueAtTime(t,this._audioContext.currentTime)}}setKeepScreenOn(){this._opt.keepScreenOn=!0}setFullscreen(t){const e=!!t;this.fullscreen!==e&&(this.fullscreen=e)}hasLoaded(){return this._hasLoaded}screenshot(t,e,r,i){return this._screenshot(t,e,r,i)}on(t,e){this._on(t,e)}},window.Jessibuca=yt):oC||(oC=!0,aC())}(); \ No newline at end of file diff --git a/web_src/static/js/jessibuca/jessibuca.d.ts b/web_src/static/js/jessibuca/jessibuca.d.ts new file mode 100644 index 00000000..b1ead7db --- /dev/null +++ b/web_src/static/js/jessibuca/jessibuca.d.ts @@ -0,0 +1,637 @@ +declare namespace Jessibuca { + + /** 超时信息 */ + enum TIMEOUT { + /** 当play()的时候,如果没有数据返回 */ + loadingTimeout = 'loadingTimeout', + /** 当播放过程中,如果超过timeout之后没有数据渲染 */ + delayTimeout = 'delayTimeout', + } + + /** 错误信息 */ + enum ERROR { + /** 播放错误,url 为空的时候,调用 play 方法 */ + playError = 'playError', + /** http 请求失败 */ + fetchError = 'fetchError', + /** websocket 请求失败 */ + websocketError = 'websocketError', + /** webcodecs 解码 h265 失败 */ + webcodecsH265NotSupport = 'webcodecsH265NotSupport', + /** mediaSource 解码 h265 失败 */ + mediaSourceH265NotSupport = 'mediaSourceH265NotSupport', + /** wasm 解码失败 */ + wasmDecodeError = 'wasmDecodeError', + } + + interface Config { + /** + * 播放器容器 + * * 若为 string ,则底层调用的是 document.getElementById('id') + * */ + container: HTMLElement | string; + /** + * 设置最大缓冲时长,单位秒,播放器会自动消除延迟 + */ + videoBuffer?: number; + /** + * worker地址 + * * 默认引用的是根目录下面的decoder.js文件 ,decoder.js 与 decoder.wasm文件必须是放在同一个目录下面。 */ + decoder?: string; + /** + * 是否不使用离屏模式(提升渲染能力) + */ + forceNoOffscreen?: boolean; + /** + * 是否开启当页面的'visibilityState'变为'hidden'的时候,自动暂停播放。 + */ + hiddenAutoPause?: boolean; + /** + * 是否有音频,如果设置`false`,则不对音频数据解码,提升性能。 + */ + hasAudio?: boolean; + /** + * 设置旋转角度,只支持,0(默认),180,270 三个值 + */ + rotate?: boolean; + /** + * 1. 当为`true`的时候:视频画面做等比缩放后,高或宽对齐canvas区域,画面不被拉伸,但有黑边。 等同于 `setScaleMode(1)` + * 2. 当为`false`的时候:视频画面完全填充canvas区域,画面会被拉伸。等同于 `setScaleMode(0)` + */ + isResize?: boolean; + /** + * 1. 当为`true`的时候:视频画面做等比缩放后,完全填充canvas区域,画面不被拉伸,没有黑边,但画面显示不全。等同于 `setScaleMode(2)` + */ + isFullSize?: boolean; + /** + * 1. 当为`true`的时候:ws协议不检验是否以.flv为依据,进行协议解析。 + */ + isFlv?: boolean; + /** + * 是否开启控制台调试打 + */ + debug?: boolean; + /** + * 1. 设置超时时长, 单位秒 + * 2. 在连接成功之前(loading)和播放中途(heart),如果超过设定时长无数据返回,则回调timeout事件 + */ + timeout?: number; + /** + * 1. 设置超时时长, 单位秒 + * 2. 在连接成功之前,如果超过设定时长无数据返回,则回调timeout事件 + */ + heartTimeout?: number; + /** + * 1. 设置超时时长, 单位秒 + * 2. 在连接成功之前,如果超过设定时长无数据返回,则回调timeout事件 + */ + loadingTimeout?: number; + /** + * 是否支持屏幕的双击事件,触发全屏,取消全屏事件 + */ + supportDblclickFullscreen?: boolean; + /** + * 是否显示网 + */ + showBandwidth?: boolean; + /** + * 配置操作按钮 + */ + operateBtns?: { + /** 是否显示全屏按钮 */ + fullscreen?: boolean; + /** 是否显示截图按钮 */ + screenshot?: boolean; + /** 是否显示播放暂停按钮 */ + play?: boolean; + /** 是否显示声音按钮 */ + audio?: boolean; + /** 是否显示录制按 */ + record?: boolean; + }; + /** + * 开启屏幕常亮,在手机浏览器上, canvas标签渲染视频并不会像video标签那样保持屏幕常亮 + */ + keepScreenOn?: boolean; + /** + * 是否开启声音,默认是关闭声音播放的 + */ + isNotMute?: boolean; + /** + * 加载过程中文案 + */ + loadingText?: boolean; + /** + * 背景图片 + */ + background?: string; + /** + * 是否开启MediaSource硬解码 + * * 视频编码只支持H.264视频(Safari on iOS不支持) + * * 不支持 forceNoOffscreen 为 false (开启离屏渲染) + */ + useMSE?: boolean; + /** + * 是否开启Webcodecs硬解码 + * * 视频编码只支持H.264视频 (需在chrome 94版本以上,需要https或者localhost环境) + * * 支持 forceNoOffscreen 为 false (开启离屏渲染) + * */ + useWCS?: boolean; + /** + * 是否开启键盘快捷键 + * 目前支持的键盘快捷键有:esc -> 退出全屏;arrowUp -> 声音增加;arrowDown -> 声音减少; + */ + hotKey?: boolean; + /** + * 在使用MSE或者Webcodecs 播放H265的时候,是否自动降级到wasm模式。 + * 设置为false 则直接关闭播放,抛出Error 异常,设置为true 则会自动切换成wasm模式播放。 + */ + autoWasm?: boolean; + /** + * heartTimeout 心跳超时之后自动再播放,不再抛出异常,而直接重新播放视频地址。 + */ + heartTimeoutReplay?: boolean, + /** + * heartTimeoutReplay 从试次数,超过之后,不再自动播放 + */ + heartTimeoutReplayTimes?: number, + /** + * loadingTimeout loading之后自动再播放,不再抛出异常,而直接重新播放视频地址。 + */ + loadingTimeoutReplay?: boolean, + /** + * heartTimeoutReplay 从试次数,超过之后,不再自动播放 + */ + loadingTimeoutReplayTimes?: number + /** + * wasm解码报错之后,不再抛出异常,而是直接重新播放视频地址。 + */ + wasmDecodeErrorReplay?: boolean, + /** + * https://github.com/langhuihui/jessibuca/issues/152 解决方案 + * 例如:WebGL图像预处理默认每次取4字节的数据,但是540x960分辨率下的U、V分量宽度是540/2=270不能被4整除,导致绿屏。 + */ + openWebglAlignment?: boolean + } +} + + +declare class Jessibuca { + + constructor(config?: Jessibuca.Config); + + /** + * 是否开启控制台调试打印 + @example + // 开启 + jessibuca.setDebug(true) + // 关闭 + jessibuca.setDebug(false) + */ + setDebug(flag: boolean): void; + + /** + * 静音 + @example + jessibuca.mute() + */ + mute(): void; + + /** + * 取消静音 + @example + jessibuca.cancelMute() + */ + cancelMute(): void; + + /** + * 留给上层用户操作来触发音频恢复的方法。 + * + * iPhone,chrome等要求自动播放时,音频必须静音,需要由一个真实的用户交互操作来恢复,不能使用代码。 + * + * https://developers.google.com/web/updates/2017/09/autoplay-policy-changes + */ + audioResume(): void; + + /** + * + * 设置超时时长, 单位秒 + * 在连接成功之前和播放中途,如果超过设定时长无数据返回,则回调timeout事件 + + @example + jessibuca.setTimeout(10) + + jessibuca.on('timeout',function(){ + // + }); + */ + setTimeout(): void; + + /** + * @param mode + * 0 视频画面完全填充canvas区域,画面会被拉伸 等同于参数 `isResize` 为false + * + * 1 视频画面做等比缩放后,高或宽对齐canvas区域,画面不被拉伸,但有黑边 等同于参数 `isResize` 为true + * + * 2 视频画面做等比缩放后,完全填充canvas区域,画面不被拉伸,没有黑边,但画面显示不全 等同于参数 `isFullResize` 为true + @example + jessibuca.setScaleMode(0) + + jessibuca.setScaleMode(1) + + jessibuca.setScaleMode(2) + */ + setScaleMode(mode: number): void; + + /** + * 暂停播放 + * + * 可以在pause 之后,再调用 `play()`方法就继续播放之前的流。 + @example + jessibuca.pause().then(()=>{ + console.log('pause success') + + jessibuca.play().then(()=>{ + + }).catch((e)=>{ + + }) + + }).catch((e)=>{ + console.log('pause error',e); + }) + */ + pause(): Promise; + + /** + * 关闭视频,不释放底层资源 + @example + jessibuca.close(); + */ + close(): void; + + /** + * 关闭视频,释放底层资源 + @example + jessibuca.destroy() + */ + destroy(): void; + + /** + * 清理画布为黑色背景 + @example + jessibuca.clearView() + */ + clearView(): void; + + /** + * 播放视频 + @example + + jessibuca.play('url').then(()=>{ + console.log('play success') + }).catch((e)=>{ + console.log('play error',e) + }) + // + jessibuca.play() + */ + play(url?: string): Promise; + + /** + * 重新调整视图大小 + */ + resize(): void; + + /** + * 设置最大缓冲时长,单位秒,播放器会自动消除延迟。 + * + * 等同于 `videoBuffer` 参数。 + * + @example + // 设置 200ms 缓冲 + jessibuca.setBufferTime(0.2) + */ + setBufferTime(time: number): void; + + /** + * 设置旋转角度,只支持,0(默认) ,180,270 三个值。 + * + * > 可用于实现监控画面小窗和全屏效果,由于iOS没有全屏API,此方法可以模拟页面内全屏效果而且多端效果一致。 * + @example + jessibuca.setRotate(0) + + jessibuca.setRotate(90) + + jessibuca.setRotate(270) + */ + setRotate(deg: number): void; + + /** + * + * 设置音量大小,取值0 — 1 + * + * > 区别于 mute 和 cancelMute 方法,虽然设置setVolume(0) 也能达到 mute方法,但是mute 方法是不调用底层播放音频的,能提高性能。而setVolume(0)只是把声音设置为0 ,以达到效果。 + * @param volume 当为0时,完全无声;当为1时,最大音量,默认值 + @example + jessibuca.setVolume(0.2) + + jessibuca.setVolume(0) + + jessibuca.setVolume(1) + */ + setVolume(volume: number): void; + + /** + * 返回是否加载完毕 + @example + var result = jessibuca.hasLoaded() + console.log(result) // true + */ + hasLoaded(): boolean; + + /** + * 开启屏幕常亮,在手机浏览器上, canvas标签渲染视频并不会像video标签那样保持屏幕常亮。 + * H5目前在chrome\edge 84, android chrome 84及以上有原生亮屏API, 需要是https页面 + * 其余平台为模拟实现,此时为兼容实现,并不保证所有浏览器都支持 + @example + jessibuca.setKeepScreenOn() + */ + setKeepScreenOn(): boolean; + + /** + * 全屏(取消全屏)播放视频 + @example + jessibuca.setFullscreen(true) + // + jessibuca.setFullscreen(false) + */ + setFullscreen(flag: boolean): void; + + /** + * + * 截图,调用后弹出下载框保存截图 + * @param filename 可选参数, 保存的文件名, 默认 `时间戳` + * @param format 可选参数, 截图的格式,可选png或jpeg或者webp ,默认 `png` + * @param quality 可选参数, 当格式是jpeg或者webp时,压缩质量,取值0 ~ 1 ,默认 `0.92` + * @param type 可选参数, 可选download或者base64或者blob,默认`download` + + @example + + jessibuca.screenshot("test","png",0.5) + + const base64 = jessibuca.screenshot("test","png",0.5,'base64') + + const fileBlob = jessibuca.screenshot("test",'blob') + */ + screenshot(filename?: string, format?: string, quality?: number, type?: string): void; + + /** + * 开始录制。 + * @param fileName 可选,默认时间戳 + * @param fileType 可选,默认webm,支持webm 和mp4 格式 + + @example + jessibuca.startRecord('xxx','webm') + */ + startRecord(fileName: string, fileType: string): void; + + /** + * 暂停录制并下载。 + @example + jessibuca.stopRecordAndSave() + */ + stopRecordAndSave(): void; + + /** + * 返回是否正在播放中状态。 + @example + var result = jessibuca.isPlaying() + console.log(result) // true + */ + isPlaying(): boolean; + + /** + * 返回是否静音。 + @example + var result = jessibuca.isMute() + console.log(result) // true + */ + isMute(): boolean; + + /** + * 返回是否正在录制。 + @example + var result = jessibuca.isRecording() + console.log(result) // true + */ + isRecording(): boolean; + + + /** + * 监听 jessibuca 初始化事件 + * @example + * jessibuca.on("load",function(){console.log('load')}) + */ + on(event: 'load', callback: () => void): void; + + /** + * 视频播放持续时间,单位ms + * @example + * jessibuca.on('timeUpdate',function (ts) {console.log('timeUpdate',ts);}) + */ + on(event: 'timeUpdate', callback: () => void): void; + + /** + * 当解析出视频信息时回调,2个回调参数 + * @example + * jessibuca.on("videoInfo",function(data){console.log('width:',data.width,'height:',data.width)}) + */ + on(event: 'videoInfo', callback: (data: { + /** 视频宽 */ + width: number; + /** 视频高 */ + height: number; + }) => void): void; + + /** + * 当解析出音频信息时回调,2个回调参数 + * @example + * jessibuca.on("audioInfo",function(data){console.log('numOfChannels:',data.numOfChannels,'sampleRate',data.sampleRate)}) + */ + on(event: 'audioInfo', callback: (data: { + /** 声频通道 */ + numOfChannels: number; + /** 采样率 */ + sampleRate: number; + }) => void): void; + + /** + * 信息,包含错误信息 + * @example + * jessibuca.on("log",function(data){console.log('data:',data)}) + */ + on(event: 'log', callback: () => void): void; + + /** + * 错误信息 + * @example + * jessibuca.on("error",function(error){ + if(error === Jessibuca.ERROR.fetchError){ + // + } + else if(error === Jessibuca.ERROR.webcodecsH265NotSupport){ + // + } + console.log('error:',error) + }) + */ + on(event: 'error', callback: (err: Jessibuca.ERROR) => void): void; + + /** + * 当前网速, 单位KB 每秒1次, + * @example + * jessibuca.on("kBps",function(data){console.log('kBps:',data)}) + */ + on(event: 'kBps', callback: (value: number) => void): void; + + /** + * 渲染开始 + * @example + * jessibuca.on("start",function(){console.log('start render')}) + */ + on(event: 'start', callback: () => void): void; + + /** + * 当设定的超时时间内无数据返回,则回调 + * @example + * jessibuca.on("timeout",function(error){console.log('timeout:',error)}) + */ + on(event: 'timeout', callback: (error: Jessibuca.TIMEOUT) => void): void; + + /** + * 当play()的时候,如果没有数据返回,则回调 + * @example + * jessibuca.on("loadingTimeout",function(){console.log('timeout')}) + */ + on(event: 'loadingTimeout', callback: () => void): void; + + /** + * 当播放过程中,如果超过timeout之后没有数据渲染,则抛出异常。 + * @example + * jessibuca.on("delayTimeout",function(){console.log('timeout')}) + */ + on(event: 'delayTimeout', callback: () => void): void; + + /** + * 当前是否全屏 + * @example + * jessibuca.on("fullscreen",function(flag){console.log('is fullscreen',flag)}) + */ + on(event: 'fullscreen', callback: () => void): void; + + /** + * 触发播放事件 + * @example + * jessibuca.on("play",function(flag){console.log('play')}) + */ + on(event: 'play', callback: () => void): void; + + /** + * 触发暂停事件 + * @example + * jessibuca.on("pause",function(flag){console.log('pause')}) + */ + on(event: 'pause', callback: () => void): void; + + /** + * 触发声音事件,返回boolean值 + * @example + * jessibuca.on("mute",function(flag){console.log('is mute',flag)}) + */ + on(event: 'mute', callback: () => void): void; + + /** + * 流状态统计,流开始播放后回调,每秒1次。 + * @example + * jessibuca.on("stats",function(s){console.log("stats is",s)}) + */ + on(event: 'stats', callback: (stats: { + /** 当前缓冲区时长,单位毫秒 */ + buf: number; + /** 当前视频帧率 */ + fps: number; + /** 当前音频码率,单位bit */ + abps: number; + /** 当前视频码率,单位bit */ + vbps: number; + /** 当前视频帧pts,单位毫秒 */ + ts: number; + }) => void): void; + + /** + * 渲染性能统计,流开始播放后回调,每秒1次。 + * @param performance 0: 表示卡顿,1: 表示流畅,2: 表示非常流程 + * @example + * jessibuca.on("performance",function(performance){console.log("performance is",performance)}) + */ + on(event: 'performance', callback: (performance: 0 | 1 | 2) => void): void; + + /** + * 录制开始的事件 + + * @example + * jessibuca.on("recordStart",function(){console.log("record start")}) + */ + on(event: 'recordStart', callback: () => void): void; + + /** + * 录制结束的事件 + + * @example + * jessibuca.on("recordEnd",function(){console.log("record end")}) + */ + on(event: 'recordEnd', callback: () => void): void; + + /** + * 录制的时候,返回的录制时长,1s一次 + + * @example + * jessibuca.on("recordingTimestamp",function(timestamp){console.log("recordingTimestamp is",timestamp)}) + */ + on(event: 'recordingTimestamp', callback: (timestamp: number) => void): void; + + /** + * 监听调用play方法 经过 初始化-> 网络请求-> 解封装 -> 解码 -> 渲染 一系列过程的时间消耗 + * @param event + * @param callback + */ + on(event: 'playToRenderTimes', callback: (times: { + playInitStart: number, // 1 初始化 + playStart: number, // 2 初始化 + streamStart: number, // 3 网络请求 + streamResponse: number, // 4 网络请求 + demuxStart: number, // 5 解封装 + decodeStart: number, // 6 解码 + videoStart: number, // 7 渲染 + playTimestamp: number,// playStart- playInitStart + streamTimestamp: number,// streamStart - playStart + streamResponseTimestamp: number,// streamResponse - streamStart + demuxTimestamp: number, // demuxStart - streamResponse + decodeTimestamp: number, // decodeStart - demuxStart + videoTimestamp: number,// videoStart - decodeStart + allTimestamp: number // videoStart - playInitStart + }) => void): void + + /** + * 监听方法 + * + @example + + jessibuca.on("load",function(){console.log('load')}) + */ + on(event: string, callback: Function): void; + +} + +export default Jessibuca; diff --git a/web_src/static/js/jessibuca/jessibuca.js b/web_src/static/js/jessibuca/jessibuca.js new file mode 100644 index 00000000..54abcbc7 --- /dev/null +++ b/web_src/static/js/jessibuca/jessibuca.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).jessibuca=t()}(this,(function(){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function t(e,t){return e(t={exports:{}},t.exports),t.exports}var i,o=t((function(e){e.exports=function(e,t,i){return t in e?Object.defineProperty(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e},e.exports.__esModule=!0,e.exports.default=e.exports})),r=(i=o)&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i;const A=0,s=1,n="flv",a="m7s",d={videoBuffer:1e3,isResize:!0,isFullResize:!1,isFlv:!1,debug:!1,hotKey:!1,loadingTimeout:10,heartTimeout:5,timeout:10,loadingTimeoutReplay:!1,heartTimeoutReplay:!1,loadingTimeoutReplayTimes:3,heartTimeoutReplayTimes:3,supportDblclickFullscreen:!1,showBandwidth:!1,keepScreenOn:!1,isNotMute:!1,hasAudio:!0,hasVideo:!0,operateBtns:{fullscreen:!1,screenshot:!1,play:!1,audio:!1,record:!1},controlAutoHide:!1,hasControl:!1,loadingText:"",background:"",decoder:"decoder.js",url:"",rotate:0,forceNoOffscreen:!0,hiddenAutoPause:!1,protocol:s,demuxType:n,useWCS:!1,useMSE:!1,useOffscreen:!1,autoWasm:!0,wasmDecodeErrorReplay:!0,openWebglAlignment:!1},c="init",l="initVideo",u="render",h="playAudio",p="initAudio",m="audioCode",g="videoCode",f="wasmError",b="Invalid NAL unit size",y=1,v=2,w=8,S=9,E="init",B="decode",C="audioDecode",R="close",k="updateConfig",T={fullscreen:"fullscreen$2",webFullscreen:"webFullscreen",decoderWorkerInit:"decoderWorkerInit",play:"play",playing:"playing",pause:"pause",mute:"mute",load:"load",loading:"loading",videoInfo:"videoInfo",timeUpdate:"timeUpdate",audioInfo:"audioInfo",log:"log",error:"error",kBps:"kBps",timeout:"timeout",delayTimeout:"delayTimeout",loadingTimeout:"loadingTimeout",stats:"stats",performance:"performance",record:"record",recording:"recording",recordingTimestamp:"recordingTimestamp",recordStart:"recordStart",recordEnd:"recordEnd",recordCreateError:"recordCreateError",buffer:"buffer",videoFrame:"videoFrame",start:"start",metadata:"metadata",resize:"resize",streamEnd:"streamEnd",streamSuccess:"streamSuccess",streamMessage:"streamMessage",streamError:"streamError",volumechange:"volumechange",destroy:"destroy",mseSourceOpen:"mseSourceOpen",mseSourceClose:"mseSourceClose",mseSourceBufferError:"mseSourceBufferError",mseSourceBufferBusy:"mseSourceBufferBusy",videoWaiting:"videoWaiting",videoTimeUpdate:"videoTimeUpdate",videoSyncAudio:"videoSyncAudio",playToRenderTimes:"playToRenderTimes"},I={load:T.load,timeUpdate:T.timeUpdate,videoInfo:T.videoInfo,audioInfo:T.audioInfo,error:T.error,kBps:T.kBps,log:T.log,start:T.start,timeout:T.timeout,loadingTimeout:T.loadingTimeout,delayTimeout:T.delayTimeout,fullscreen:"fullscreen",play:T.play,pause:T.pause,mute:T.mute,stats:T.stats,performance:T.performance,recordingTimestamp:T.recordingTimestamp,recordStart:T.recordStart,recordEnd:T.recordEnd,playToRenderTimes:T.playToRenderTimes},x={playError:"playIsNotPauseOrUrlIsNull",fetchError:"fetchError",websocketError:"websocketError",webcodecsH265NotSupport:"webcodecsH265NotSupport",mediaSourceH265NotSupport:"mediaSourceH265NotSupport",wasmDecodeError:"wasmDecodeError"},D="notConnect",j="open",L="close",U="error",F={download:"download",base64:"base64",blob:"blob"},V={7:"H264(AVC)",12:"H265(HEVC)"},O=7,M=12,Q={10:"AAC",7:"ALAW",8:"MULAW"},W=32,G=33,J=34,N=0,P=1,z=2,Y="mp4",H="webm",X="webcodecs",Z="webgl",q="offscreen",K="key",_="delta",$='video/mp4; codecs="avc1.64002A"',ee="ended",te="open",ie="closed",oe=1e3,re=27,Ae=38,se=40;class ne{constructor(e){this.log=function(t){if(e._opt.debug){for(var i=arguments.length,o=new Array(i>1?i-1:0),r=1;r1?i-1:0),r=1;r1?i-1:0),r=1;r3&&void 0!==arguments[3]?arguments[3]:{};if(!e)return;if(Array.isArray(t))return t.map((t=>this.proxy(e,t,i,o)));e.addEventListener(t,i,o);const r=()=>e.removeEventListener(t,i,o);return this.destroys.push(r),r}destroy(){this.master.debug&&this.master.debug.log("Events","destroy"),this.destroys.forEach((e=>e()))}}var de=t((function(e){!function(){var t="undefined"!=typeof window&&void 0!==window.document?window.document:{},i=e.exports,o=function(){for(var e,i=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError"]],o=0,r=i.length,A={};o0&&void 0!==arguments[0]?arguments[0]:"";const t=e.split(","),i=atob(t[1]),o=t[0].replace("data:","").replace(";base64","");let r=i.length,A=new Uint8Array(r);for(;r--;)A[r]=i.charCodeAt(r);return new File([A],"file",{type:o})}function ue(e,t){const i=document.createElement("a");i.download=t,i.href=URL.createObjectURL(e),i.click(),setTimeout((()=>{URL.revokeObjectURL(e)}),ve()?1e3:0)}function he(){return(new Date).getTime()}function pe(e,t,i){return Math.max(Math.min(e,Math.max(t,i)),Math.min(t,i))}function me(e,t,i){if(e)return"object"==typeof t&&Object.keys(t).forEach((i=>{me(e,i,t[i])})),e.style[t]=i,e}function ge(e,t){let i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(!e)return 0;const o=getComputedStyle(e,null).getPropertyValue(t);return i?parseFloat(o):o}function fe(){return performance&&"function"==typeof performance.now?performance.now():Date.now()}function be(e){let t=0,i=fe();return o=>{t+=o;const r=fe(),A=r-i;A>=1e3&&(e(t/A*1e3),i=r,t=0)}}function ye(){return/iphone|ipod|android.*mobile|windows.*phone|blackberry.*mobile/i.test(window.navigator.userAgent.toLowerCase())}function ve(){const e=window.navigator.userAgent.toLowerCase();return e&&/iphone|ipad|ipod|ios/.test(e)}function we(e){if(null==e||""===e)return"0 KB/S";let t=parseFloat(e);return t=t.toFixed(2),t+"KB/S"}function Se(e){return null==e}function Ee(e){return!Se(e)}de.isEnabled,(()=>{try{if("object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate){const e=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));if(e instanceof WebAssembly.Module)return new WebAssembly.Instance(e)instanceof WebAssembly.Instance}}catch(e){}})();class Be{on(e,t,i){const o=this.e||(this.e={});return(o[e]||(o[e]=[])).push({fn:t,ctx:i}),this}once(e,t,i){const o=this;function r(){o.off(e,r);for(var A=arguments.length,s=new Array(A),n=0;n1?i-1:0),r=1;r{delete i[e]})),void delete this.e;const o=i[e],r=[];if(o&&t)for(let e=0,i=o.length;e{var i=["attribute vec4 vertexPos;","attribute vec4 texturePos;","varying vec2 textureCoord;","void main()","{","gl_Position = vertexPos;","textureCoord = texturePos.xy;","}"].join("\n"),o=["precision highp float;","varying highp vec2 textureCoord;","uniform sampler2D ySampler;","uniform sampler2D uSampler;","uniform sampler2D vSampler;","const mat4 YUV2RGB = mat4","(","1.1643828125, 0, 1.59602734375, -.87078515625,","1.1643828125, -.39176171875, -.81296875, .52959375,","1.1643828125, 2.017234375, 0, -1.081390625,","0, 0, 0, 1",");","void main(void) {","highp float y = texture2D(ySampler, textureCoord).r;","highp float u = texture2D(uSampler, textureCoord).r;","highp float v = texture2D(vSampler, textureCoord).r;","gl_FragColor = vec4(y, u, v, 1) * YUV2RGB;","}"].join("\n");t&&e.pixelStorei(e.UNPACK_ALIGNMENT,1);var r=e.createShader(e.VERTEX_SHADER);e.shaderSource(r,i),e.compileShader(r),e.getShaderParameter(r,e.COMPILE_STATUS)||console.log("Vertex shader failed to compile: "+e.getShaderInfoLog(r));var A=e.createShader(e.FRAGMENT_SHADER);e.shaderSource(A,o),e.compileShader(A),e.getShaderParameter(A,e.COMPILE_STATUS)||console.log("Fragment shader failed to compile: "+e.getShaderInfoLog(A));var s=e.createProgram();e.attachShader(s,r),e.attachShader(s,A),e.linkProgram(s),e.getProgramParameter(s,e.LINK_STATUS)||console.log("Program failed to compile: "+e.getProgramInfoLog(s)),e.useProgram(s);var n=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,n),e.bufferData(e.ARRAY_BUFFER,new Float32Array([1,1,-1,1,1,-1,-1,-1]),e.STATIC_DRAW);var a=e.getAttribLocation(s,"vertexPos");e.enableVertexAttribArray(a),e.vertexAttribPointer(a,2,e.FLOAT,!1,0,0);var d=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,d),e.bufferData(e.ARRAY_BUFFER,new Float32Array([1,0,0,0,1,1,0,1]),e.STATIC_DRAW);var c=e.getAttribLocation(s,"texturePos");function l(t,i){var o=e.createTexture();return e.bindTexture(e.TEXTURE_2D,o),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.bindTexture(e.TEXTURE_2D,null),e.uniform1i(e.getUniformLocation(s,t),i),o}e.enableVertexAttribArray(c),e.vertexAttribPointer(c,2,e.FLOAT,!1,0,0);var u=l("ySampler",0),h=l("uSampler",1),p=l("vSampler",2);return{render:function(t,i,o,r,A){e.viewport(0,0,t,i),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,u),e.texImage2D(e.TEXTURE_2D,0,e.LUMINANCE,t,i,0,e.LUMINANCE,e.UNSIGNED_BYTE,o),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,h),e.texImage2D(e.TEXTURE_2D,0,e.LUMINANCE,t/2,i/2,0,e.LUMINANCE,e.UNSIGNED_BYTE,r),e.activeTexture(e.TEXTURE2),e.bindTexture(e.TEXTURE_2D,p),e.texImage2D(e.TEXTURE_2D,0,e.LUMINANCE,t/2,i/2,0,e.LUMINANCE,e.UNSIGNED_BYTE,A),e.drawArrays(e.TRIANGLE_STRIP,0,4)},destroy:function(){try{e.deleteProgram(s),e.deleteBuffer(n),e.deleteBuffer(d),e.deleteTexture(u),e.deleteTexture(h),e.deleteBuffer(p)}catch(e){}}}})(this.contextGl,this.player._opt.openWebglAlignment);this.contextGlRender=e.render,this.contextGlDestroy=e.destroy}_initContext2D(){this.context2D=this.$videoElement.getContext("2d")}_initCanvasRender(){this.player._opt.useWCS&&!this._supportOffscreen()?(this.renderType=X,this._initContext2D()):this._supportOffscreen()?(this.renderType=q,this._bindOffscreen()):(this.renderType=Z,this._initContextGl())}_supportOffscreen(){return"function"==typeof this.$videoElement.transferControlToOffscreen&&this.player._opt.useOffscreen}_bindOffscreen(){this.bitmaprenderer=this.$videoElement.getContext("bitmaprenderer")}initCanvasViewSize(){this.$videoElement.width=this.videoInfo.width,this.$videoElement.height=this.videoInfo.height,this.resize()}render(e){switch(this.player.videoTimestamp=e.ts,this.renderType){case q:this.bitmaprenderer.transferFromImageBitmap(e.buffer);break;case Z:this.contextGlRender(this.$videoElement.width,this.$videoElement.height,e.output[0],e.output[1],e.output[2]);break;case X:this.context2D.drawImage(e.videoFrame,0,0,this.$videoElement.width,this.$videoElement.height)}}screenshot(e,t,i,o){e=e||he(),o=o||F.download;const r={png:"image/png",jpeg:"image/jpeg",webp:"image/webp"};let A=.92;!r[t]&&F[t]&&(o=t,t="png",i=void 0),"string"==typeof i&&(o=i,i=void 0),void 0!==i&&(A=Number(i));const s=this.$videoElement.toDataURL(r[t]||r.png,A),n=le(s);return o===F.base64?s:o===F.blob?n:void(o===F.download&&ue(n,e))}clearView(){switch(this.renderType){case q:(function(e,t){const i=document.createElement("canvas");return i.width=e,i.height=t,createImageBitmap(i,0,0,e,t)})(this.$videoElement.width,this.$videoElement.height).then((e=>{this.bitmaprenderer.transferFromImageBitmap(e)}));break;case Z:this.contextGl.clear(this.contextGl.COLOR_BUFFER_BIT);break;case X:this.context2D.clearRect(0,0,this.$videoElement.width,this.$videoElement.height)}}resize(){this.player.debug.log("canvasVideo","resize");const e=this.player._opt;let t=this.player.width,i=this.player.height;e.hasControl&&!e.controlAutoHide&&(ye()&&this.player.fullscreen?t-=38:i-=38);let o=this.$videoElement.width,r=this.$videoElement.height;const A=e.rotate;let s=(t-o)/2,n=(i-r)/2;270!==A&&90!==A||(o=this.$videoElement.height,r=this.$videoElement.width);const a=t/o,d=i/r;let c=a>d?d:a;e.isResize||a!==d&&(c=a+","+d),e.isFullResize&&(c=a>d?a:d);let l="scale("+c+")";A&&(l+=" rotate("+A+"deg)"),this.$videoElement.style.transform=l,this.$videoElement.style.left=s+"px",this.$videoElement.style.top=n+"px"}}class ke extends Ce{constructor(e){super(),this.player=e;const t=document.createElement("video");t.muted=!0,t.style.position="absolute",t.style.top=0,t.style.left=0,e.$container.appendChild(t),this.$videoElement=t,this.videoInfo={width:"",height:"",encType:""},this.resize();const{proxy:i}=this.player.events;i(this.$videoElement,"canplay",(()=>{this.player.debug.log("Video","canplay")})),i(this.$videoElement,"waiting",(()=>{this.player.emit(T.videoWaiting)})),i(this.$videoElement,"timeupdate",(e=>{})),this.player.debug.log("Video","init")}destroy(){this.player.$container.removeChild(this.$videoElement),this.$videoElement=null,this.init=!1,this.off(),this.player.debug.log("Video","destroy")}play(){this.$videoElement.play()}clearView(){}screenshot(e,t,i,o){e=e||he(),o=o||F.download;let r=.92;!{png:"image/png",jpeg:"image/jpeg",webp:"image/webp"}[t]&&F[t]&&(o=t,t="png",i=void 0),"string"==typeof i&&(o=i,i=void 0),void 0!==i&&(r=Number(i));const A=this.$videoElement;let s=document.createElement("canvas");s.width=A.videoWidth,s.height=A.videoHeight;s.getContext("2d").drawImage(A,0,0,s.width,s.height);const n=s.toDataURL(F[t]||F.png,r),a=le(n);return o===F.base64?n:o===F.blob?a:void(o===F.download&&ue(a,e))}initCanvasViewSize(){this.resize()}resize(){let e=this.player.width,t=this.player.height;const i=this.player._opt,o=i.rotate;i.hasControl&&!i.controlAutoHide&&(ye()&&this.player.fullscreen?e-=38:t-=38),this.$videoElement.width=e,this.$videoElement.height=t,270!==o&&90!==o||(this.$videoElement.width=t,this.$videoElement.height=e);let r=(e-this.$videoElement.width)/2,A=(t-this.$videoElement.height)/2,s="contain";i.isResize||(s="fill"),i.isFullResize&&(s="none"),this.$videoElement.style.objectFit=s,this.$videoElement.style.transform="rotate("+o+"deg)",this.$videoElement.style.left=r+"px",this.$videoElement.style.top=A+"px"}}class Te{constructor(e){return new(Te.getLoaderFactory(e._opt))(e)}static getLoaderFactory(e){return e.useMSE?ke:Re}}class Ie extends Be{constructor(e){super(),this.bufferList=[],this.player=e,this.scriptNode=null,this.hasInitScriptNode=!1,this.audioContextChannel=null,this.audioContext=new(window.AudioContext||window.webkitAudioContext),this.gainNode=this.audioContext.createGain();const t=this.audioContext.createBufferSource();t.buffer=this.audioContext.createBuffer(1,1,22050),t.connect(this.audioContext.destination),t.noteOn?t.noteOn(0):t.start(0),this.audioBufferSourceNode=t,this.mediaStreamAudioDestinationNode=this.audioContext.createMediaStreamDestination(),this.audioEnabled(!0),this.gainNode.gain.value=0,this.playing=!1,this.audioSyncVideoOption={diff:null},this.audioInfo={encType:"",channels:"",sampleRate:""},this.init=!1,this.hasAudio=!1,this.on(T.videoSyncAudio,(e=>{this.audioSyncVideoOption=e})),this.player.debug.log("AudioContext","init")}destroy(){this.closeAudio(),this.audioContext.close(),this.audioContext=null,this.gainNode=null,this.init=!1,this.hasAudio=!1,this.playing=!1,this.scriptNode&&(this.scriptNode.onaudioprocess=ce,this.scriptNode=null),this.audioBufferSourceNode=null,this.mediaStreamAudioDestinationNode=null,this.hasInitScriptNode=!1,this.audioSyncVideoOption={diff:null},this.audioInfo={encType:"",channels:"",sampleRate:""},this.off(),this.player.debug.log("AudioContext","destroy")}updateAudioInfo(e){e.encTypeCode&&(this.audioInfo.encType=Q[e.encTypeCode]),e.channels&&(this.audioInfo.channels=e.channels),e.sampleRate&&(this.audioInfo.sampleRate=e.sampleRate),this.audioInfo.sampleRate&&this.audioInfo.channels&&this.audioInfo.encType&&!this.init&&(this.player.emit(T.audioInfo,this.audioInfo),this.init=!0)}get isPlaying(){return this.playing}get isMute(){return 0===this.gainNode.gain.value||this.isStateSuspended()}get volume(){return this.gainNode.gain.value}get bufferSize(){return this.bufferList.length}initScriptNode(){if(this.playing=!0,this.hasInitScriptNode)return;const e=this.audioInfo.channels,t=this.audioContext.createScriptProcessor(1024,0,e);t.onaudioprocess=t=>{const i=t.outputBuffer;if(this.bufferList.length&&this.playing){if(!this.player._opt.useWCS&&!this.player._opt.useMSE){if(this.audioSyncVideoOption.diff>oe)return void this.player.debug.warn("AudioContext",`audioSyncVideoOption more than diff :${this.audioSyncVideoOption.diff}, waiting`);if(this.audioSyncVideoOption.diff<-1e3){this.player.debug.warn("AudioContext",`audioSyncVideoOption less than diff :${this.audioSyncVideoOption.diff}, dropping`);let e=this.bufferList.shift();for(;e.ts-this.player.videoTimestamp<-1e3&&this.bufferList.length>0;)e=this.bufferList.shift();if(0===this.bufferList.length)return}}if(0===this.bufferList.length)return;const t=this.bufferList.shift();t&&t.ts&&(this.player.audioTimestamp=t.ts);for(let o=0;o20&&(this.player.debug.warn("AudioContext",`bufferList is large: ${this.bufferList.length}`),this.bufferList.length>50&&this.bufferList.shift()))}pause(){this.audioSyncVideoOption={diff:null},this.playing=!1,this.clear()}resume(){this.playing=!0}}class xe{constructor(e){return new(xe.getLoaderFactory())(e)}static getLoaderFactory(){return Ie}}class De extends Be{constructor(e){super(),this.player=e,this.playing=!1,this.abortController=new AbortController,this.streamRate=be((t=>{e.emit(T.kBps,(t/1024).toFixed(2))})),e.debug.log("FetchStream","init")}destroy(){this.abort(),this.off(),this.streamRate=null,this.player.debug.log("FetchStream","destroy")}fetchStream(e){const{demux:t}=this.player;this.player._times.streamStart=he(),fetch(e,{signal:this.abortController.signal}).then((e=>{const i=e.body.getReader();this.emit(T.streamSuccess);const o=()=>{i.read().then((e=>{let{done:i,value:r}=e;i?t.close():(this.streamRate&&this.streamRate(r.byteLength),t.dispatch(r),o())})).catch((e=>{t.close(),this.emit(x.fetchError,e),this.player.emit(T.error,x.fetchError),this.abort()}))};o()})).catch((e=>{this.abort(),this.emit(x.fetchError,e),this.player.emit(T.error,x.fetchError)}))}abort(){this.abortController&&(this.abortController.abort(),this.abortController=null)}}class je extends Be{constructor(e){super(),this.player=e,this.socket=null,this.socketStatus=D,this.wsUrl=null,this.streamRate=be((t=>{e.emit(T.kBps,(t/1024).toFixed(2))}))}destroy(){this.socket&&(this.socket.close(),this.socket=null),this.socketStatus=D,this.streamRate=null,this.wsUrl=null,this.off(),this.player.debug.log("websocketLoader","destroy")}_createWebSocket(){const e=this.player,{debug:t,events:{proxy:i},demux:o}=e;this.socket=new WebSocket(this.wsUrl),this.socket.binaryType="arraybuffer",i(this.socket,"open",(()=>{this.emit(T.streamSuccess),t.log("websocketLoader","socket open"),this.socketStatus=j})),i(this.socket,"message",(e=>{this.streamRate&&this.streamRate(e.data.byteLength),this._handleMessage(e.data)})),i(this.socket,"close",(()=>{t.log("websocketLoader","socket close"),this.emit(T.streamEnd),this.socketStatus=L})),i(this.socket,"error",(e=>{t.log("websocketLoader","socket error"),this.emit(x.websocketError,e),this.player.emit(T.error,x.websocketError),this.socketStatus=U,o.close(),t.log("websocketLoader","socket error:",e)}))}_handleMessage(e){const{demux:t}=this.player;t?t.dispatch(e):this.player.debug.warn("websocketLoader","websocket handle message demux is null")}fetchStream(e){this.player._times.streamStart=he(),this.wsUrl=e,this._createWebSocket()}}class Le{constructor(e){return new(Le.getLoaderFactory(e._opt.protocol))(e)}static getLoaderFactory(e){return e===s?De:e===A?je:void 0}}var Ue=t((function(t){function i(e,t){if(!e)throw"First parameter is required.";t=new o(e,t=t||{type:"video"});var A=this;function s(i){i&&(t.initCallback=function(){i(),i=t.initCallback=null});var o=new r(e,t);(h=new o(e,t)).record(),u("recording"),t.disableLogs||console.log("Initialized recorderType:",h.constructor.name,"for output-type:",t.type)}function n(e){if(e=e||function(){},h){if("paused"===A.state)return A.resumeRecording(),void setTimeout((function(){n(e)}),1);"recording"===A.state||t.disableLogs||console.warn('Recording state should be: "recording", however current state is: ',A.state),t.disableLogs||console.log("Stopped recording "+t.type+" stream."),"gif"!==t.type?h.stop(i):(h.stop(),i()),u("stopped")}else m();function i(i){if(h){Object.keys(h).forEach((function(e){"function"!=typeof h[e]&&(A[e]=h[e])}));var o=h.blob;if(!o){if(!i)throw"Recording failed.";h.blob=o=i}if(o&&!t.disableLogs&&console.log(o.type,"->",b(o.size)),e){var r;try{r=l.createObjectURL(o)}catch(e){}"function"==typeof e.call?e.call(A,r):e(r)}t.autoWriteToDisk&&d((function(e){var i={};i[t.type+"Blob"]=e,x.Store(i)}))}else"function"==typeof e.call?e.call(A,""):e("")}}function a(e){postMessage((new FileReaderSync).readAsDataURL(e))}function d(e,i){if(!e)throw"Pass a callback function over getDataURL.";var o=i?i.blob:(h||{}).blob;if(!o)return t.disableLogs||console.warn("Blob encoder did not finish its job yet."),void setTimeout((function(){d(e,i)}),1e3);if("undefined"==typeof Worker||navigator.mozGetUserMedia){var r=new FileReader;r.readAsDataURL(o),r.onload=function(t){e(t.target.result)}}else{var A=function(e){try{var t=l.createObjectURL(new Blob([e.toString(),"this.onmessage = function (eee) {"+e.name+"(eee.data);}"],{type:"application/javascript"})),i=new Worker(t);return l.revokeObjectURL(t),i}catch(e){}}(a);A.onmessage=function(t){e(t.data)},A.postMessage(o)}}function c(e){e=e||0,"paused"!==A.state?"stopped"!==A.state&&(e>=A.recordingDuration?n(A.onRecordingStopped):(e+=1e3,setTimeout((function(){c(e)}),1e3))):setTimeout((function(){c(e)}),1e3)}function u(e){A&&(A.state=e,"function"==typeof A.onStateChanged.call?A.onStateChanged.call(A,e):A.onStateChanged(e))}var h,p='It seems that recorder is destroyed or "startRecording" is not invoked for '+t.type+" recorder.";function m(){!0!==t.disableLogs&&console.warn(p)}var g={startRecording:function(i){return t.disableLogs||console.log("RecordRTC version: ",A.version),i&&(t=new o(e,i)),t.disableLogs||console.log("started recording "+t.type+" stream."),h?(h.clearRecordedData(),h.record(),u("recording"),A.recordingDuration&&c(),A):(s((function(){A.recordingDuration&&c()})),A)},stopRecording:n,pauseRecording:function(){h?"recording"===A.state?(u("paused"),h.pause(),t.disableLogs||console.log("Paused recording.")):t.disableLogs||console.warn("Unable to pause the recording. Recording state: ",A.state):m()},resumeRecording:function(){h?"paused"===A.state?(u("recording"),h.resume(),t.disableLogs||console.log("Resumed recording.")):t.disableLogs||console.warn("Unable to resume the recording. Recording state: ",A.state):m()},initRecorder:s,setRecordingDuration:function(e,t){if(void 0===e)throw"recordingDuration is required.";if("number"!=typeof e)throw"recordingDuration must be a number.";return A.recordingDuration=e,A.onRecordingStopped=t||function(){},{onRecordingStopped:function(e){A.onRecordingStopped=e}}},clearRecordedData:function(){h?(h.clearRecordedData(),t.disableLogs||console.log("Cleared old recorded data.")):m()},getBlob:function(){if(h)return h.blob;m()},getDataURL:d,toURL:function(){if(h)return l.createObjectURL(h.blob);m()},getInternalRecorder:function(){return h},save:function(e){h?y(h.blob,e):m()},getFromDisk:function(e){h?i.getFromDisk(t.type,e):m()},setAdvertisementArray:function(e){t.advertisement=[];for(var i=e.length,o=0;o-1&&"netscape"in window&&/ rv:/.test(navigator.userAgent),m=!h&&!u&&!!navigator.webkitGetUserMedia||v()||-1!==navigator.userAgent.toLowerCase().indexOf("chrome/"),g=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);g&&!m&&-1!==navigator.userAgent.indexOf("CriOS")&&(g=!1,m=!0);var f=window.MediaStream;function b(e){if(0===e)return"0 Bytes";var t=parseInt(Math.floor(Math.log(e)/Math.log(1e3)),10);return(e/Math.pow(1e3,t)).toPrecision(3)+" "+["Bytes","KB","MB","GB","TB"][t]}function y(e,t){if(!e)throw"Blob object is required.";if(!e.type)try{e.type="video/webm"}catch(e){}var i=(e.type||"video/webm").split("/")[1];if(-1!==i.indexOf(";")&&(i=i.split(";")[0]),t&&-1!==t.indexOf(".")){var o=t.split(".");t=o[0],i=o[1]}var r=(t||Math.round(9999999999*Math.random())+888888888)+"."+i;if(void 0!==navigator.msSaveOrOpenBlob)return navigator.msSaveOrOpenBlob(e,r);if(void 0!==navigator.msSaveBlob)return navigator.msSaveBlob(e,r);var A=document.createElement("a");A.href=l.createObjectURL(e),A.download=r,A.style="display:none;opacity:0;color:transparent;",(document.body||document.documentElement).appendChild(A),"function"==typeof A.click?A.click():(A.target="_blank",A.dispatchEvent(new MouseEvent("click",{view:window,bubbles:!0,cancelable:!0}))),l.revokeObjectURL(A.href)}function v(){return"undefined"!=typeof window&&"object"==typeof window.process&&"renderer"===window.process.type||(!("undefined"==typeof process||"object"!=typeof process.versions||!process.versions.electron)||"object"==typeof navigator&&"string"==typeof navigator.userAgent&&navigator.userAgent.indexOf("Electron")>=0)}function w(e,t){return e&&e.getTracks?e.getTracks().filter((function(e){return e.kind===(t||"audio")})):[]}function S(e,t){"srcObject"in t?t.srcObject=e:"mozSrcObject"in t?t.mozSrcObject=e:t.srcObject=e}void 0===f&&"undefined"!=typeof webkitMediaStream&&(f=webkitMediaStream),void 0!==f&&void 0===f.prototype.stop&&(f.prototype.stop=function(){this.getTracks().forEach((function(e){e.stop()}))}),i.invokeSaveAsDialog=y,i.getTracks=w,i.getSeekableBlob=function(e,t){if("undefined"==typeof EBML)throw new Error("Please link: https://www.webrtc-experiment.com/EBML.js");var i=new EBML.Reader,o=new EBML.Decoder,r=EBML.tools,A=new FileReader;A.onload=function(e){o.decode(this.result).forEach((function(e){i.read(e)})),i.stop();var A=r.makeMetadataSeekable(i.metadatas,i.duration,i.cues),s=this.result.slice(i.metadataSize),n=new Blob([A,s],{type:"video/webm"});t(n)},A.readAsArrayBuffer(e)},i.bytesToSize=b,i.isElectron=v;var E={};function B(){if(p||g||u)return!0;var e,t,i=navigator.userAgent,o=""+parseFloat(navigator.appVersion),r=parseInt(navigator.appVersion,10);return(m||h)&&(e=i.indexOf("Chrome"),o=i.substring(e+7)),-1!==(t=o.indexOf(";"))&&(o=o.substring(0,t)),-1!==(t=o.indexOf(" "))&&(o=o.substring(0,t)),r=parseInt(""+o,10),isNaN(r)&&(o=""+parseFloat(navigator.appVersion),r=parseInt(navigator.appVersion,10)),r>=49}function C(e,t){var i=this;if(void 0===e)throw'First argument "MediaStream" is required.';if("undefined"==typeof MediaRecorder)throw"Your browser does not support the Media Recorder API. Please try other modules e.g. WhammyRecorder or StereoAudioRecorder.";if("audio"===(t=t||{mimeType:"video/webm"}).type){var o;if(w(e,"video").length&&w(e,"audio").length)navigator.mozGetUserMedia?(o=new f).addTrack(w(e,"audio")[0]):o=new f(w(e,"audio")),e=o;t.mimeType&&-1!==t.mimeType.toString().toLowerCase().indexOf("audio")||(t.mimeType=m?"audio/webm":"audio/ogg"),t.mimeType&&"audio/ogg"!==t.mimeType.toString().toLowerCase()&&navigator.mozGetUserMedia&&(t.mimeType="audio/ogg")}var r,A=[];function s(){i.timestamps.push((new Date).getTime()),"function"==typeof t.onTimeStamp&&t.onTimeStamp(i.timestamps[i.timestamps.length-1],i.timestamps)}function n(e){return r&&r.mimeType?r.mimeType:e.mimeType||"video/webm"}function a(){A=[],r=null,i.timestamps=[]}this.getArrayOfBlobs=function(){return A},this.record=function(){i.blob=null,i.clearRecordedData(),i.timestamps=[],d=[],A=[];var o=t;t.disableLogs||console.log("Passing following config over MediaRecorder API.",o),r&&(r=null),m&&!B()&&(o="video/vp8"),"function"==typeof MediaRecorder.isTypeSupported&&o.mimeType&&(MediaRecorder.isTypeSupported(o.mimeType)||(t.disableLogs||console.warn("MediaRecorder API seems unable to record mimeType:",o.mimeType),o.mimeType="audio"===t.type?"audio/webm":"video/webm"));try{r=new MediaRecorder(e,o),t.mimeType=o.mimeType}catch(t){r=new MediaRecorder(e)}o.mimeType&&!MediaRecorder.isTypeSupported&&"canRecordMimeType"in r&&!1===r.canRecordMimeType(o.mimeType)&&(t.disableLogs||console.warn("MediaRecorder API seems unable to record mimeType:",o.mimeType)),r.ondataavailable=function(e){if(e.data&&d.push("ondataavailable: "+b(e.data.size)),"number"!=typeof t.timeSlice)!e.data||!e.data.size||e.data.size<100||i.blob?i.recordingCallback&&(i.recordingCallback(new Blob([],{type:n(o)})),i.recordingCallback=null):(i.blob=t.getNativeBlob?e.data:new Blob([e.data],{type:n(o)}),i.recordingCallback&&(i.recordingCallback(i.blob),i.recordingCallback=null));else if(e.data&&e.data.size&&(A.push(e.data),s(),"function"==typeof t.ondataavailable)){var r=t.getNativeBlob?e.data:new Blob([e.data],{type:n(o)});t.ondataavailable(r)}},r.onstart=function(){d.push("started")},r.onpause=function(){d.push("paused")},r.onresume=function(){d.push("resumed")},r.onstop=function(){d.push("stopped")},r.onerror=function(e){e&&(e.name||(e.name="UnknownError"),d.push("error: "+e),t.disableLogs||(-1!==e.name.toString().toLowerCase().indexOf("invalidstate")?console.error("The MediaRecorder is not in a state in which the proposed operation is allowed to be executed.",e):-1!==e.name.toString().toLowerCase().indexOf("notsupported")?console.error("MIME type (",o.mimeType,") is not supported.",e):-1!==e.name.toString().toLowerCase().indexOf("security")?console.error("MediaRecorder security error",e):"OutOfMemory"===e.name?console.error("The UA has exhaused the available memory. User agents SHOULD provide as much additional information as possible in the message attribute.",e):"IllegalStreamModification"===e.name?console.error("A modification to the stream has occurred that makes it impossible to continue recording. An example would be the addition of a Track while recording is occurring. User agents SHOULD provide as much additional information as possible in the message attribute.",e):"OtherRecordingError"===e.name?console.error("Used for an fatal error other than those listed above. User agents SHOULD provide as much additional information as possible in the message attribute.",e):"GenericError"===e.name?console.error("The UA cannot provide the codec or recording option that has been requested.",e):console.error("MediaRecorder Error",e)),function(e){if(!i.manuallyStopped&&r&&"inactive"===r.state)return delete t.timeslice,void r.start(6e5);setTimeout(void 0,1e3)}(),"inactive"!==r.state&&"stopped"!==r.state&&r.stop())},"number"==typeof t.timeSlice?(s(),r.start(t.timeSlice)):r.start(36e5),t.initCallback&&t.initCallback()},this.timestamps=[],this.stop=function(e){e=e||function(){},i.manuallyStopped=!0,r&&(this.recordingCallback=e,"recording"===r.state&&r.stop(),"number"==typeof t.timeSlice&&setTimeout((function(){i.blob=new Blob(A,{type:n(t)}),i.recordingCallback(i.blob)}),100))},this.pause=function(){r&&"recording"===r.state&&r.pause()},this.resume=function(){r&&"paused"===r.state&&r.resume()},this.clearRecordedData=function(){r&&"recording"===r.state&&i.stop(a),a()},this.getInternalRecorder=function(){return r},this.blob=null,this.getState=function(){return r&&r.state||"inactive"};var d=[];this.getAllStates=function(){return d},void 0===t.checkForInactiveTracks&&(t.checkForInactiveTracks=!1);i=this;!function o(){if(r&&!1!==t.checkForInactiveTracks)return!1===function(){if("active"in e){if(!e.active)return!1}else if("ended"in e&&e.ended)return!1;return!0}()?(t.disableLogs||console.log("MediaStream seems stopped."),void i.stop()):void setTimeout(o,1e3)}(),this.name="MediaStreamRecorder",this.toString=function(){return this.name}}function R(e,t){if(!w(e,"audio").length)throw"Your stream has no audio tracks.";var o,r=this,A=[],s=[],n=!1,a=0,d=2,c=(t=t||{}).desiredSampRate;function u(){if(!1===t.checkForInactiveTracks)return!0;if("active"in e){if(!e.active)return!1}else if("ended"in e&&e.ended)return!1;return!0}function h(e,t){function i(e,t){var i,o=e.numberOfAudioChannels,r=e.leftBuffers.slice(0),A=e.rightBuffers.slice(0),s=e.sampleRate,n=e.internalInterleavedLength,a=e.desiredSampRate;function d(e,t,i){var o=Math.round(e.length*(t/i)),r=[],A=Number((e.length-1)/(o-1));r[0]=e[0];for(var s=1;s96e3)&&(t.disableLogs||console.log("sample-rate must be under range 22050 and 96000.")),t.disableLogs||t.desiredSampRate&&console.log("Desired sample-rate: "+t.desiredSampRate);var y=!1;function v(){A=[],s=[],a=0,E=!1,n=!1,y=!1,p=null,r.leftchannel=A,r.rightchannel=s,r.numberOfAudioChannels=d,r.desiredSampRate=c,r.sampleRate=b,r.recordingLength=a,B={left:[],right:[],recordingLength:0}}function S(){o&&(o.onaudioprocess=null,o.disconnect(),o=null),m&&(m.disconnect(),m=null),v()}this.pause=function(){y=!0},this.resume=function(){if(!1===u())throw"Please make sure MediaStream is active.";if(!n)return t.disableLogs||console.log("Seems recording has been restarted."),void this.record();y=!1},this.clearRecordedData=function(){t.checkForInactiveTracks=!1,n&&this.stop(S),S()},this.name="StereoAudioRecorder",this.toString=function(){return this.name};var E=!1;o.onaudioprocess=function(e){if(!y)if(!1===u()&&(t.disableLogs||console.log("MediaStream seems stopped."),o.disconnect(),n=!1),n){E||(E=!0,t.onAudioProcessStarted&&t.onAudioProcessStarted(),t.initCallback&&t.initCallback());var i=e.inputBuffer.getChannelData(0),c=new Float32Array(i);if(A.push(c),2===d){var l=e.inputBuffer.getChannelData(1),h=new Float32Array(l);s.push(h)}a+=f,r.recordingLength=a,void 0!==t.timeSlice&&(B.recordingLength+=f,B.left.push(c),2===d&&B.right.push(h))}else m&&(m.disconnect(),m=null)},p.createMediaStreamDestination?o.connect(p.createMediaStreamDestination()):o.connect(p.destination),this.leftchannel=A,this.rightchannel=s,this.numberOfAudioChannels=d,this.desiredSampRate=c,this.sampleRate=b,r.recordingLength=a;var B={left:[],right:[],recordingLength:0};function C(){n&&"function"==typeof t.ondataavailable&&void 0!==t.timeSlice&&(B.left.length?(h({desiredSampRate:c,sampleRate:b,numberOfAudioChannels:d,internalInterleavedLength:B.recordingLength,leftBuffers:B.left,rightBuffers:1===d?[]:B.right},(function(e,i){var o=new Blob([i],{type:"audio/wav"});t.ondataavailable(o),setTimeout(C,t.timeSlice)})),B={left:[],right:[],recordingLength:0}):setTimeout(C,t.timeSlice))}}function k(e,t){if("undefined"==typeof html2canvas)throw"Please link: https://www.webrtc-experiment.com/screenshot.js";(t=t||{}).frameInterval||(t.frameInterval=10);var i=!1;["captureStream","mozCaptureStream","webkitCaptureStream"].forEach((function(e){e in document.createElement("canvas")&&(i=!0)}));var o,r,A,s=!(!window.webkitRTCPeerConnection&&!window.webkitGetUserMedia||!window.chrome),n=50,a=navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);if(s&&a&&a[2]&&(n=parseInt(a[2],10)),s&&n<52&&(i=!1),t.useWhammyRecorder&&(i=!1),i)if(t.disableLogs||console.log("Your browser supports both MediRecorder API and canvas.captureStream!"),e instanceof HTMLCanvasElement)o=e;else{if(!(e instanceof CanvasRenderingContext2D))throw"Please pass either HTMLCanvasElement or CanvasRenderingContext2D.";o=e.canvas}else navigator.mozGetUserMedia&&(t.disableLogs||console.error("Canvas recording is NOT supported in Firefox."));this.record=function(){if(A=!0,i&&!t.useWhammyRecorder){var e;"captureStream"in o?e=o.captureStream(25):"mozCaptureStream"in o?e=o.mozCaptureStream(25):"webkitCaptureStream"in o&&(e=o.webkitCaptureStream(25));try{var s=new f;s.addTrack(w(e,"video")[0]),e=s}catch(e){}if(!e)throw"captureStream API are NOT available.";(r=new C(e,{mimeType:t.mimeType||"video/webm"})).record()}else h.frames=[],u=(new Date).getTime(),l();t.initCallback&&t.initCallback()},this.getWebPImages=function(i){if("canvas"===e.nodeName.toLowerCase()){var o=h.frames.length;h.frames.forEach((function(e,i){var r=o-i;t.disableLogs||console.log(r+"/"+o+" frames remaining"),t.onEncodingCallback&&t.onEncodingCallback(r,o);var A=e.image.toDataURL("image/webp",1);h.frames[i].image=A})),t.disableLogs||console.log("Generating WebM"),i()}else i()},this.stop=function(e){A=!1;var o=this;i&&r?r.stop(e):this.getWebPImages((function(){h.compile((function(i){t.disableLogs||console.log("Recording finished!"),o.blob=i,o.blob.forEach&&(o.blob=new Blob([],{type:"video/webm"})),e&&e(o.blob),h.frames=[]}))}))};var d=!1;function c(){h.frames=[],A=!1,d=!1}function l(){if(d)return u=(new Date).getTime(),setTimeout(l,500);if("canvas"===e.nodeName.toLowerCase()){var i=(new Date).getTime()-u;return u=(new Date).getTime(),h.frames.push({image:(o=document.createElement("canvas"),r=o.getContext("2d"),o.width=e.width,o.height=e.height,r.drawImage(e,0,0),o),duration:i}),void(A&&setTimeout(l,t.frameInterval))}var o,r;html2canvas(e,{grabMouse:void 0===t.showMousePointer||t.showMousePointer,onrendered:function(e){var i=(new Date).getTime()-u;if(!i)return setTimeout(l,t.frameInterval);u=(new Date).getTime(),h.frames.push({image:e.toDataURL("image/webp",1),duration:i}),A&&setTimeout(l,t.frameInterval)}})}this.pause=function(){d=!0,r instanceof C&&r.pause()},this.resume=function(){d=!1,r instanceof C?r.resume():A||this.record()},this.clearRecordedData=function(){A&&this.stop(c),c()},this.name="CanvasRecorder",this.toString=function(){return this.name};var u=(new Date).getTime(),h=new I.Video(100)}function T(e,t){function i(e){e=void 0!==e?e:10;var t=(new Date).getTime()-a;return t?A?(a=(new Date).getTime(),setTimeout(i,100)):(a=(new Date).getTime(),n.paused&&n.play(),l.drawImage(n,0,0,c.width,c.height),d.frames.push({duration:t,image:c.toDataURL("image/webp")}),void(r||setTimeout(i,e,e))):setTimeout(i,e,e)}function o(e,t,i,o,r){var A=document.createElement("canvas");A.width=c.width,A.height=c.height;var s,n,a,d=A.getContext("2d"),l=[],u=-1===t,h=t&&t>0&&t<=e.length?t:e.length,p=0,m=0,g=0,f=Math.sqrt(Math.pow(255,2)+Math.pow(255,2)+Math.pow(255,2)),b=i&&i>=0&&i<=1?i:0,y=o&&o>=0&&o<=1?o:0,v=!1;n=-1,a=(s={length:h,functionToLoop:function(t,i){var o,r,A,s=function(){!v&&A-o<=A*y||(u&&(v=!0),l.push(e[i])),t()};if(v)s();else{var n=new Image;n.onload=function(){d.drawImage(n,0,0,c.width,c.height);var e=d.getImageData(0,0,c.width,c.height);o=0,r=e.data.length,A=e.data.length/4;for(var t=0;t127)throw"TrackNumber > 127 not supported";return[128|e.trackNum,e.timecode>>8,255&e.timecode,t].map((function(e){return String.fromCharCode(e)})).join("")+e.frame}({discardable:0,frame:e.data.slice(4),invisible:0,keyframe:1,lacing:0,trackNum:1,timecode:Math.round(t)});return t+=e.duration,{data:i,id:163}})))}function i(e){for(var t=[];e>0;)t.push(255&e),e>>=8;return new Uint8Array(t.reverse())}function o(e){var t=[];e=(e.length%8?new Array(9-e.length%8).join("0"):"")+e;for(var i=0;i1?2*A[0].width:A[0].width;var n=1;3!==e&&4!==e||(n=2),5!==e&&6!==e||(n=3),7!==e&&8!==e||(n=4),9!==e&&10!==e||(n=5),r.height=A[0].height*n}else r.width=s.width||360,r.height=s.height||240;t&&t instanceof HTMLVideoElement&&u(t),A.forEach((function(e,t){u(e,t)})),setTimeout(l,s.frameInterval)}}function u(e,t){if(!o){var i=0,r=0,s=e.width,n=e.height;1===t&&(i=e.width),2===t&&(r=e.height),3===t&&(i=e.width,r=e.height),4===t&&(r=2*e.height),5===t&&(i=e.width,r=2*e.height),6===t&&(r=3*e.height),7===t&&(i=e.width,r=3*e.height),void 0!==e.stream.left&&(i=e.stream.left),void 0!==e.stream.top&&(r=e.stream.top),void 0!==e.stream.width&&(s=e.stream.width),void 0!==e.stream.height&&(n=e.stream.height),A.drawImage(e,i,r,s,n),"function"==typeof e.stream.onRender&&e.stream.onRender(A,i,r,s,n,t)}}function h(e){var i=document.createElement("video");return function(e,t){"srcObject"in t?t.srcObject=e:"mozSrcObject"in t?t.mozSrcObject=e:t.srcObject=e}(e,i),i.className=t,i.muted=!0,i.volume=0,i.width=e.width||s.width||360,i.height=e.height||s.height||240,i.play(),i}function p(t){i=[],(t=t||e).forEach((function(e){if(e.getTracks().filter((function(e){return"video"===e.kind})).length){var t=h(e);t.stream=e,i.push(t)}}))}void 0!==n?c.AudioContext=n:"undefined"!=typeof webkitAudioContext&&(c.AudioContext=webkitAudioContext),this.startDrawingFrames=function(){l()},this.appendStreams=function(t){if(!t)throw"First parameter is required.";t instanceof Array||(t=[t]),t.forEach((function(t){var o=new d;if(t.getTracks().filter((function(e){return"video"===e.kind})).length){var r=h(t);r.stream=t,i.push(r),o.addTrack(t.getTracks().filter((function(e){return"video"===e.kind}))[0])}if(t.getTracks().filter((function(e){return"audio"===e.kind})).length){var A=s.audioContext.createMediaStreamSource(t);s.audioDestination=s.audioContext.createMediaStreamDestination(),A.connect(s.audioDestination),o.addTrack(s.audioDestination.stream.getTracks().filter((function(e){return"audio"===e.kind}))[0])}e.push(o)}))},this.releaseStreams=function(){i=[],o=!0,s.gainNode&&(s.gainNode.disconnect(),s.gainNode=null),s.audioSources.length&&(s.audioSources.forEach((function(e){e.disconnect()})),s.audioSources=[]),s.audioDestination&&(s.audioDestination.disconnect(),s.audioDestination=null),s.audioContext&&s.audioContext.close(),s.audioContext=null,A.clearRect(0,0,r.width,r.height),r.stream&&(r.stream.stop(),r.stream=null)},this.resetVideoStreams=function(e){!e||e instanceof Array||(e=[e]),p(e)},this.name="MultiStreamsMixer",this.toString=function(){return this.name},this.getMixedStream=function(){o=!1;var t=function(){var e;p(),"captureStream"in r?e=r.captureStream():"mozCaptureStream"in r?e=r.mozCaptureStream():s.disableLogs||console.error("Upgrade to latest Chrome or otherwise enable this flag: chrome://flags/#enable-experimental-web-platform-features");var t=new d;return e.getTracks().filter((function(e){return"video"===e.kind})).forEach((function(e){t.addTrack(e)})),r.stream=t,t}(),i=function(){c.AudioContextConstructor||(c.AudioContextConstructor=new c.AudioContext);s.audioContext=c.AudioContextConstructor,s.audioSources=[],!0===s.useGainNode&&(s.gainNode=s.audioContext.createGain(),s.gainNode.connect(s.audioContext.destination),s.gainNode.gain.value=0);var t=0;if(e.forEach((function(e){if(e.getTracks().filter((function(e){return"audio"===e.kind})).length){t++;var i=s.audioContext.createMediaStreamSource(e);!0===s.useGainNode&&i.connect(s.gainNode),s.audioSources.push(i)}})),!t)return;return s.audioDestination=s.audioContext.createMediaStreamDestination(),s.audioSources.forEach((function(e){e.connect(s.audioDestination)})),s.audioDestination.stream}();return i&&i.getTracks().filter((function(e){return"audio"===e.kind})).forEach((function(e){t.addTrack(e)})),e.forEach((function(e){e.fullcanvas})),t}}function L(e,t){e=e||[];var i,o,r=this;(t=t||{elementClass:"multi-streams-mixer",mimeType:"video/webm",video:{width:360,height:240}}).frameInterval||(t.frameInterval=10),t.video||(t.video={}),t.video.width||(t.video.width=360),t.video.height||(t.video.height=240),this.record=function(){var r;i=new j(e,t.elementClass||"multi-streams-mixer"),(r=[],e.forEach((function(e){w(e,"video").forEach((function(e){r.push(e)}))})),r).length&&(i.frameInterval=t.frameInterval||10,i.width=t.video.width||360,i.height=t.video.height||240,i.startDrawingFrames()),t.previewStream&&"function"==typeof t.previewStream&&t.previewStream(i.getMixedStream()),(o=new C(i.getMixedStream(),t)).record()},this.stop=function(e){o&&o.stop((function(t){r.blob=t,e(t),r.clearRecordedData()}))},this.pause=function(){o&&o.pause()},this.resume=function(){o&&o.resume()},this.clearRecordedData=function(){o&&(o.clearRecordedData(),o=null),i&&(i.releaseStreams(),i=null)},this.addStreams=function(r){if(!r)throw"First parameter is required.";r instanceof Array||(r=[r]),e.concat(r),o&&i&&(i.appendStreams(r),t.previewStream&&"function"==typeof t.previewStream&&t.previewStream(i.getMixedStream()))},this.resetVideoStreams=function(e){i&&(!e||e instanceof Array||(e=[e]),i.resetVideoStreams(e))},this.getMixer=function(){return i},this.name="MultiStreamRecorder",this.toString=function(){return this.name}}function U(e,t){var i,o,r;function A(){return new ReadableStream({start:function(o){var r=document.createElement("canvas"),A=document.createElement("video"),s=!0;A.srcObject=e,A.muted=!0,A.height=t.height,A.width=t.width,A.volume=0,A.onplaying=function(){r.width=t.width,r.height=t.height;var e=r.getContext("2d"),n=1e3/t.frameRate,a=setInterval((function(){if(i&&(clearInterval(a),o.close()),s&&(s=!1,t.onVideoProcessStarted&&t.onVideoProcessStarted()),e.drawImage(A,0,0),"closed"!==o._controlledReadableStream.state)try{o.enqueue(e.getImageData(0,0,t.width,t.height))}catch(e){}}),n)},A.play()}})}function s(e,a){if(!t.workerPath&&!a)return i=!1,void fetch("https://unpkg.com/webm-wasm@latest/dist/webm-worker.js").then((function(t){t.arrayBuffer().then((function(t){s(e,t)}))}));if(!t.workerPath&&a instanceof ArrayBuffer){var d=new Blob([a],{type:"text/javascript"});t.workerPath=l.createObjectURL(d)}t.workerPath||console.error("workerPath parameter is missing."),(o=new Worker(t.workerPath)).postMessage(t.webAssemblyPath||"https://unpkg.com/webm-wasm@latest/dist/webm-wasm.wasm"),o.addEventListener("message",(function(e){"READY"===e.data?(o.postMessage({width:t.width,height:t.height,bitrate:t.bitrate||1200,timebaseDen:t.frameRate||30,realtime:t.realtime}),A().pipeTo(new WritableStream({write:function(e){i?console.error("Got image, but recorder is finished!"):o.postMessage(e.data.buffer,[e.data.buffer])}}))):e.data&&(r||n.push(e.data))}))}"undefined"!=typeof ReadableStream&&"undefined"!=typeof WritableStream||console.error("Following polyfill is strongly recommended: https://unpkg.com/@mattiasbuelens/web-streams-polyfill/dist/polyfill.min.js"),(t=t||{}).width=t.width||640,t.height=t.height||480,t.frameRate=t.frameRate||30,t.bitrate=t.bitrate||1200,t.realtime=t.realtime||!0,this.record=function(){n=[],r=!1,this.blob=null,s(e),"function"==typeof t.initCallback&&t.initCallback()},this.pause=function(){r=!0},this.resume=function(){r=!1};var n=[];this.stop=function(e){i=!0;var t=this;!function(e){o?(o.addEventListener("message",(function(t){null===t.data&&(o.terminate(),o=null,e&&e())})),o.postMessage(null)):e&&e()}((function(){t.blob=new Blob(n,{type:"video/webm"}),e(t.blob)}))},this.name="WebAssemblyRecorder",this.toString=function(){return this.name},this.clearRecordedData=function(){n=[],r=!1,this.blob=null},this.blob=null}i.DiskStorage=x,i.GifRecorder=D,i.MultiStreamRecorder=L,i.RecordRTCPromisesHandler=function(e,t){if(!this)throw'Use "new RecordRTCPromisesHandler()"';if(void 0===e)throw'First argument "MediaStream" is required.';var o=this;o.recordRTC=new i(e,t),this.startRecording=function(){return new Promise((function(e,t){try{o.recordRTC.startRecording(),e()}catch(e){t(e)}}))},this.stopRecording=function(){return new Promise((function(e,t){try{o.recordRTC.stopRecording((function(i){o.blob=o.recordRTC.getBlob(),o.blob&&o.blob.size?e(i):t("Empty blob.",o.blob)}))}catch(e){t(e)}}))},this.pauseRecording=function(){return new Promise((function(e,t){try{o.recordRTC.pauseRecording(),e()}catch(e){t(e)}}))},this.resumeRecording=function(){return new Promise((function(e,t){try{o.recordRTC.resumeRecording(),e()}catch(e){t(e)}}))},this.getDataURL=function(e){return new Promise((function(e,t){try{o.recordRTC.getDataURL((function(t){e(t)}))}catch(e){t(e)}}))},this.getBlob=function(){return new Promise((function(e,t){try{e(o.recordRTC.getBlob())}catch(e){t(e)}}))},this.getInternalRecorder=function(){return new Promise((function(e,t){try{e(o.recordRTC.getInternalRecorder())}catch(e){t(e)}}))},this.reset=function(){return new Promise((function(e,t){try{e(o.recordRTC.reset())}catch(e){t(e)}}))},this.destroy=function(){return new Promise((function(e,t){try{e(o.recordRTC.destroy())}catch(e){t(e)}}))},this.getState=function(){return new Promise((function(e,t){try{e(o.recordRTC.getState())}catch(e){t(e)}}))},this.blob=null,this.version="5.6.2"},i.WebAssemblyRecorder=U}));class Fe extends Be{constructor(e){super(),this.player=e,this.fileName="",this.fileType=H,this.isRecording=!1,this.recordingTimestamp=0,this.recordingInterval=null,e.debug.log("Recorder","init")}destroy(){this._reset(),this.player.debug.log("Recorder","destroy")}setFileName(e,t){this.fileName=e,Y!==t&&H!==t||(this.fileType=t)}get recording(){return this.isRecording}get recordTime(){return this.recordingTimestamp}startRecord(){const e=this.player.debug,t={type:"video",mimeType:"video/webm;codecs=h264",onTimeStamp:t=>{e.log("Recorder","record timestamp :"+t)},disableLogs:!this.player._opt.debug};try{const e=this.player.video.$videoElement.captureStream(25);if(this.player.audio&&this.player.audio.mediaStreamAudioDestinationNode&&this.player.audio.mediaStreamAudioDestinationNode.stream&&!this.player.audio.isStateSuspended()&&this.player.audio.hasAudio&&this.player._opt.hasAudio){const t=this.player.audio.mediaStreamAudioDestinationNode.stream;if(t.getAudioTracks().length>0){const i=t.getAudioTracks()[0];i&&i.enabled&&e.addTrack(i)}}this.recorder=Ue(e,t)}catch(t){e.error("Recorder",t),this.emit(T.recordCreateError)}this.recorder&&(this.isRecording=!0,this.emit(T.recording,!0),this.recorder.startRecording(),e.log("Recorder","start recording"),this.player.emit(T.recordStart),this.recordingInterval=window.setInterval((()=>{this.recordingTimestamp+=1,this.player.emit(T.recordingTimestamp,this.recordingTimestamp)}),1e3))}stopRecordAndSave(){this.recorder&&this.isRecording&&this.recorder.stopRecording((()=>{this.player.debug.log("Recorder","stop recording"),this.player.emit(T.recordEnd),function(e,t,i){const o=window.URL.createObjectURL(e),r=document.createElement("a");r.href=o,r.download=(t||he())+"."+(i||H),r.click(),setTimeout((()=>{window.URL.revokeObjectURL(o)}),ve()?1e3:0)}(this.recorder.getBlob(),this.fileName,this.fileType),this._reset(),this.emit(T.recording,!1)}))}_reset(){this.isRecording=!1,this.recordingTimestamp=0,this.recorder&&(this.recorder.destroy(),this.recorder=null),this.fileName=null,this.recordingInterval&&clearInterval(this.recordingInterval),this.recordingInterval=null}}class Ve{constructor(e){return new(Ve.getLoaderFactory())(e)}static getLoaderFactory(){return Fe}}class Oe{constructor(e){this.player=e,this.decoderWorker=new Worker(e._opt.decoder),this._initDecoderWorker(),e.debug.log("decoderWorker","init")}destroy(){this.decoderWorker.postMessage({cmd:R}),this.decoderWorker.terminate(),this.decoderWorker=null,this.player.debug.log("decoderWorker","destroy")}_initDecoderWorker(){const{debug:e,events:{proxy:t}}=this.player;this.decoderWorker.onmessage=t=>{const i=t.data;switch(i.cmd){case c:e.log("decoderWorker","onmessage:",c),this.player.loaded||this.player.emit(T.load),this.player.emit(T.decoderWorkerInit),this._initWork();break;case g:e.log("decoderWorker","onmessage:",g,i.code),this.player._times.decodeStart||(this.player._times.decodeStart=he()),this.player.video.updateVideoInfo({encTypeCode:i.code});break;case m:e.log("decoderWorker","onmessage:",m,i.code),this.player.audio&&this.player.audio.updateAudioInfo({encTypeCode:i.code});break;case l:e.log("decoderWorker","onmessage:",l,`width:${i.w},height:${i.h}`),this.player.video.updateVideoInfo({width:i.w,height:i.h}),this.player.video.initCanvasViewSize();break;case p:e.log("decoderWorker","onmessage:",p,`channels:${i.channels},sampleRate:${i.sampleRate}`),this.player.audio&&(this.player.audio.updateAudioInfo(i),this.player.audio.initScriptNode(i));break;case u:this.player.handleRender(),this.player.video.render(i),this.player.emit(T.timeUpdate,i.ts),this.player.updateStats({fps:!0,ts:i.ts,buf:i.delay}),this.player._times.videoStart||(this.player._times.videoStart=he(),this.player.handlePlayToRenderTimes());break;case h:this.player.playing&&this.player.audio&&this.player.audio.play(i.buffer,i.ts);break;case f:i.message&&-1!==i.message.indexOf(b)&&(this.player.emit(T.error,x.wasmDecodeError),this.player.emit(x.wasmDecodeError));break;default:this.player[i.cmd]&&this.player[i.cmd](i)}}}_initWork(){const e={debug:this.player._opt.debug,forceNoOffscreen:this.player._opt.forceNoOffscreen,useWCS:this.player._opt.useWCS,videoBuffer:this.player._opt.videoBuffer,openWebglAlignment:this.player._opt.openWebglAlignment};this.decoderWorker.postMessage({cmd:E,opt:JSON.stringify(e),sampleRate:this.player.audio&&this.player.audio.audioContext.sampleRate||0})}decodeVideo(e,t,i){const o={type:v,ts:Math.max(t,0),isIFrame:i};this.decoderWorker.postMessage({cmd:B,buffer:e,options:o},[e.buffer])}decodeAudio(e,t){this.player._opt.useWCS&&!this.player._opt.useOffscreen||this.player._opt.useMSE?this._decodeAudioNoDelay(e,t):this._decodeAudio(e,t)}_decodeAudio(e,t){const i={type:y,ts:Math.max(t,0)};this.decoderWorker.postMessage({cmd:B,buffer:e,options:i},[e.buffer])}_decodeAudioNoDelay(e,t){this.decoderWorker.postMessage({cmd:C,buffer:e,ts:Math.max(t,0)},[e.buffer])}updateWorkConfig(e){this.decoderWorker.postMessage({cmd:k,key:e.key,value:e.value})}}class Me extends Be{constructor(e){super(),this.player=e,this.stopId=null,this.firstTimestamp=null,this.startTimestamp=null,this.delay=-1,this.bufferList=[],this.dropping=!1,this.initInterval()}destroy(){this.stopId&&(clearInterval(this.stopId),this.stopId=null),this.firstTimestamp=null,this.startTimestamp=null,this.delay=-1,this.bufferList=[],this.dropping=!1,this.off()}getDelay(e){return e?(this.firstTimestamp?e&&(this.delay=Date.now()-this.startTimestamp-(e-this.firstTimestamp)):(this.firstTimestamp=e,this.startTimestamp=Date.now(),this.delay=-1),this.delay):-1}resetDelay(){this.firstTimestamp=null,this.startTimestamp=null,this.delay=-1,this.dropping=!1}initInterval(){this.player.debug.log("common dumex","init Interval");let e=()=>{let e;const t=this.player._opt.videoBuffer;if(this.bufferList.length)if(this.dropping){for(e=this.bufferList.shift(),e.type===y&&0===e.payload[1]&&this._doDecoderDecode(e);!e.isIFrame&&this.bufferList.length;)e=this.bufferList.shift(),e.type===y&&0===e.payload[1]&&this._doDecoderDecode(e);e.isIFrame&&(this.dropping=!1,this._doDecoderDecode(e))}else if(e=this.bufferList[0],-1===this.getDelay(e.ts))this.bufferList.shift(),this._doDecoderDecode(e);else if(this.delay>t+1e3)this.resetDelay(),this.dropping=!0;else for(;this.bufferList.length&&(e=this.bufferList[0],this.getDelay(e.ts)>t);)this.bufferList.shift(),this._doDecoderDecode(e)};e(),this.stopId=setInterval(e,10)}_doDecode(e,t,i,o){const r=this.player;let A={ts:i,type:t,isIFrame:!1};r._opt.useWCS&&!r._opt.useOffscreen||r._opt.useMSE?(t===v&&(A.isIFrame=o),this.pushBuffer(e,A)):t===v?r.decoderWorker&&r.decoderWorker.decodeVideo(e,i,o):t===y&&r._opt.hasAudio&&r.decoderWorker&&r.decoderWorker.decodeAudio(e,i)}_doDecoderDecode(e){const t=this.player,{webcodecsDecoder:i,mseDecoder:o}=t;e.type===y?t._opt.hasAudio&&t.decoderWorker&&t.decoderWorker.decodeAudio(e.payload,e.ts):e.type===v&&(t._opt.useWCS&&!t._opt.useOffscreen?i.decodeVideo(e.payload,e.ts,e.isIFrame):t._opt.useMSE&&o.decodeVideo(e.payload,e.ts,e.isIFrame))}pushBuffer(e,t){t.type===y?this.bufferList.push({ts:t.ts,payload:e,type:y}):t.type===v&&this.bufferList.push({ts:t.ts,payload:e,type:v,isIFrame:t.isIFrame})}close(){}}class Qe extends Me{constructor(e){super(e),this.input=this._inputFlv(),this.flvDemux=this.dispatchFlvData(this.input),e.debug.log("FlvDemux","init")}destroy(){super.destroy(),this.input=null,this.flvDemux=null,this.player.debug.log("FlvDemux","destroy")}dispatch(e){this.flvDemux(e)}*_inputFlv(){yield 9;const e=new ArrayBuffer(4),t=new Uint8Array(e),i=new Uint32Array(e),o=this.player;for(;;){t[3]=0;const e=yield 15,r=e[4];t[0]=e[7],t[1]=e[6],t[2]=e[5];const A=i[0];t[0]=e[10],t[1]=e[9],t[2]=e[8];let s=i[0];16777215===s&&(t[3]=e[11],s=i[0]);const n=yield A;switch(r){case w:o._opt.hasAudio&&(o.updateStats({abps:n.byteLength}),n.byteLength>0&&this._doDecode(n,y,s));break;case S:if(o._times.demuxStart||(o._times.demuxStart=he()),o._opt.hasVideo){o.updateStats({vbps:n.byteLength});const e=n[0]>>4==1;n.byteLength>0&&this._doDecode(n,v,s,e)}}}}dispatchFlvData(e){let t=e.next(),i=null;return o=>{let r=new Uint8Array(o);if(i){let e=new Uint8Array(i.length+r.length);e.set(i),e.set(r,i.length),r=e,i=null}for(;r.length>=t.value;){let i=r.slice(t.value);t=e.next(r.slice(0,t.value)),r=i}r.length>0&&(i=r)}}close(){this.input&&this.input.return(null)}}class We extends Me{constructor(e){super(e),e.debug.log("M7sDemux","init")}destroy(){super.destroy(),this.player.debug.log("M7sDemux","destroy")}dispatch(e){const t=this.player,i=new DataView(e),o=i.getUint8(0),r=i.getUint32(1,!1);switch(o){case y:if(t._opt.hasAudio){const i=new Uint8Array(e,5);t.updateStats({abps:i.byteLength}),i.byteLength>0&&this._doDecode(i,o,r)}break;case v:if(t._opt.hasVideo&&(t._times.demuxStart||(t._times.demuxStart=he()),i.byteLength>5)){const A=new Uint8Array(e,5),s=i.getUint8(5)>>4==1;t.updateStats({vbps:A.byteLength}),A.byteLength>0&&this._doDecode(A,o,r,s)}}}}class Ge{constructor(e){return new(Ge.getLoaderFactory(e._opt.demuxType))(e)}static getLoaderFactory(e){return e===a?We:e===n?Qe:void 0}}class Je extends Be{constructor(e){super(),this.player=e,this.hasInit=!1,this.isInitInfo=!1,this.decoder=null,this.initDecoder(),e.debug.log("Webcodecs","init")}destroy(){this.decoder&&(this.decoder.close(),this.decoder=null),this.hasInit=!1,this.isInitInfo=!1,this.off(),this.player.debug.log("Webcodecs","destroy")}initDecoder(){const e=this;this.decoder=new VideoDecoder({output(t){e.handleDecode(t)},error(t){e.handleError(t)}})}handleDecode(e){this.isInitInfo||(this.player.video.updateVideoInfo({width:e.codedWidth,height:e.codedHeight}),this.player.video.initCanvasViewSize(),this.isInitInfo=!0),this.player._times.videoStart||(this.player._times.videoStart=he(),this.player.handlePlayToRenderTimes()),this.player.handleRender(),this.player.video.render({videoFrame:e}),this.player.updateStats({fps:!0,ts:0,buf:this.player.demux.delay}),setTimeout((function(){e.close?e.close():e.destroy()}),100)}handleError(e){this.player.debug.log("Webcodecs","VideoDecoder handleError",e)}decodeVideo(e,t,i){if(this.hasInit){const o=new EncodedVideoChunk({data:e.slice(5),timestamp:t,type:i?K:_});this.decoder.decode(o)}else if(i&&0===e[1]){const t=15&e[0];if(this.player.video.updateVideoInfo({encTypeCode:t}),t===M)return void this.emit(x.webcodecsH265NotSupport);this.player._times.decodeStart||(this.player._times.decodeStart=he());const i=function(e){let t=e.subarray(1,4),i="avc1.";for(let e=0;e<3;e++){let o=t[e].toString(16);o.length<2&&(o="0"+o),i+=o}return{codec:i,description:e}}(e.slice(5));this.decoder.configure(i),this.hasInit=!0}}}const Ne={play:"播放",pause:"暂停",audio:"",mute:"",screenshot:"截图",loading:"加载",fullscreen:"全屏",fullscreenExit:"退出全屏",record:"录制",recordStop:"停止录制"};var Pe=Object.keys(Ne).reduce(((e,t)=>(e[t]=`\n \n ${Ne[t]?`${Ne[t]}`:""}\n`,e)),{}),ze=(e,t)=>{const{events:{proxy:i}}=e,o=document.createElement("object");o.setAttribute("aria-hidden","true"),o.setAttribute("tabindex",-1),o.type="text/html",o.data="about:blank",me(o,{display:"block",position:"absolute",top:"0",left:"0",height:"100%",width:"100%",overflow:"hidden",pointerEvents:"none",zIndex:"-1"});let r=e.width,A=e.height;i(o,"load",(()=>{i(o.contentDocument.defaultView,"resize",(()=>{e.width===r&&e.height===A||(r=e.width,A=e.height,e.emit(T.resize))}))})),e.$container.appendChild(o),e.on(T.destroy,(()=>{e.$container.removeChild(o)})),e.on(T.volumechange,(()=>{!function(e){if(0===e)me(t.$volumeOn,"display","none"),me(t.$volumeOff,"display","flex"),me(t.$volumeHandle,"top","48px");else if(t.$volumeHandle&&t.$volumePanel){const i=ge(t.$volumePanel,"height")||60,o=ge(t.$volumeHandle,"height"),r=i-(i-o)*e-o;me(t.$volumeHandle,"top",`${r}px`),me(t.$volumeOn,"display","flex"),me(t.$volumeOff,"display","none")}t.$volumePanelText&&(t.$volumePanelText.innerHTML=parseInt(100*e))}(e.volume)})),e.on(T.loading,(e=>{me(t.$loading,"display",e?"flex":"none"),me(t.$poster,"display","none"),e&&me(t.$playBig,"display","none")}));const s=i=>{let o=!0===(r=i)||!1===r?i:e.fullscreen;var r;me(t.$fullscreenExit,"display",o?"flex":"none"),me(t.$fullscreen,"display",o?"none":"flex")};try{de.on("change",s),e.events.destroys.push((()=>{de.off("change",s)}))}catch(e){}e.on(T.webFullscreen,(e=>{s(e)})),e.on(T.recording,(()=>{me(t.$record,"display",e.recording?"none":"flex"),me(t.$recordStop,"display",e.recording?"flex":"none")})),e.on(T.recordingTimestamp,(e=>{})),e.on(T.playing,(e=>{me(t.$play,"display",e?"none":"flex"),me(t.$playBig,"display",e?"none":"block"),me(t.$pause,"display",e?"flex":"none"),me(t.$screenshot,"display",e?"flex":"none"),me(t.$record,"display",e?"flex":"none"),me(t.$fullscreen,"display",e?"flex":"none"),e||t.$speed&&(t.$speed.innerHTML=we(""))})),e.on(T.kBps,(e=>{const i=we(e);t.$speed&&(t.$speed.innerHTML=i)}))};function Ye(e,t){void 0===t&&(t={});var i=t.insertAt;if(e&&"undefined"!=typeof document){var o=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css","top"===i&&o.firstChild?o.insertBefore(r,o.firstChild):o.appendChild(r),r.styleSheet?r.styleSheet.cssText=e:r.appendChild(document.createTextNode(e))}}Ye('@keyframes rotation{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}.jessibuca-container .jessibuca-icon{cursor:pointer;width:16px;height:16px}.jessibuca-container .jessibuca-poster{position:absolute;z-index:10;left:0;top:0;right:0;bottom:0;height:100%;width:100%;background-position:50%;background-repeat:no-repeat;background-size:contain;pointer-events:none}.jessibuca-container .jessibuca-play-big{position:absolute;display:none;height:100%;width:100%;background:rgba(0,0,0,.4)}.jessibuca-container .jessibuca-play-big:after{cursor:pointer;content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);display:block;width:48px;height:48px;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAACgklEQVRoQ+3ZPYsTQRjA8eeZZCFlWttAwCIkZOaZJt8hlvkeHrlccuAFT6wEG0FQOeQQLCIWih6chQgKgkkKIyqKCVYip54IWmiQkTmyYhFvd3Zn3yDb7szu/7cv7GaDkPEFM94PK0DSZ9DzDAyHw7uI2HRDlVJX5/N5r9FoHCYdr/fvCRiNRmpJ6AEidoUQ15NG+AH8BgD2n9AHANAmohdJQfwAfgGA4xF4bjabnW21Whob62ILoKNfAsAGEd2PU2ATcNSNiDf0/cE5/xAHxDpgEf0NADaJ6HLUiKgAbvcjpdSGlPJZVJCoAUfdSqkLxWLxTLlc/mkbEgtgET1TSnWklLdtIuIEuN23crlcp16vv7cBSQKgu38AwBYRXQyLSArg3hsjRDxNRE+CQhIF/BN9qVAobFYqle+mkLQAdLd+8K0T0U0TRJoAbvc9fVkJId75gaQRoLv1C2STiPTb7rFLWgE6+g0RncwyYEJEtawCvjDGmpzzp5kD6NfxfD7frtVqB17xen2a7oG3ALBm+oMoFQBEPD+dTvtBfpImDXjIGFvjnD/3c7ksG5MU4HDxWeZa0HB3XhKAXcdxOn5vUi9gnIDXSqm2lHLPK8pkfVyAbSLqm4T5HRs1YB8RO0KIid8g03FRAT4rpbpSyh3TINPxUQB2GGM9zvkn05gg420CJovLZT9ISNA5tgB9ItoOGhFmnh/AcZ/X9xhj65zzV2Eiwsz1A1j2B8dHAOgS0W6YnduY6wkYj8d3lFKn/j66Ea84jtOrVqtfbQSE3YYnYDAY5Eql0hYAnNDv6kKIx2F3anO+J8DmzqLY1goQxVE12ebqDJgcrSjGrs5AFEfVZJt/AF0m+jHzUTtnAAAAAElFTkSuQmCC");background-repeat:no-repeat;background-position:50%}.jessibuca-container .jessibuca-play-big:hover:after{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAACEElEQVRoQ+2ZXStEQRjH/3/yIXwDdz7J+i7kvdisXCk3SiFJW27kglBcSFFKbqwQSa4krykuKB09Naf2Yndn5jgzc06d53Znd36/mWfeniVyHsw5PwqB0DOonYEoijYBlOpAFwCMkHwLDS/9mwhEDUCfAAyTXA4tYSLwC6CtCegegH6S56FETAR+AHRoACcBTJAUWa+RloBAXwAYIrnt0yBNgZi7qtbHgw8RFwLC/QFglOScawlXAjH3gUqrE1cirgVi7mkAYyS/0xbxJSDcdwAGSa6nKeFTIOZeUyL3aYiEEBDuLwDjJGf+KxFKIOY+BdBL8iipSGiBmHtWbbuftiJZERBuOfgGSK7aSGRJIObeUml1ayKSRQHhlgtkiaTcdltGVgUE+ppkV54FaiS78yrwqlLoOI8Cch2XV548W7WRpTVwA6DP9kGUFYEpAOUkT9LQAvtq1M+0udKkQSgBqSlJWWYxKXj8vRACK+o6bbRIdYI+Ba7U7rKjg7L53JdAhWTZBsy0rWuBXZUuNVMg23auBF7UIl2yBbJt70JAoKV6/WwLk6R9mgKSJlJ1kLTxFmkJyCla8UZd15GJQKvyumyJ8gy8DAEvfZoINPqD41EtUjmUgoaJwAaAnjrKebVI34OSq85NBNqlCAWgE0CV5GEWwI3vQlmCbcSinYFCwPEIFDPgeIC1P1/MgHaIHDf4Aydx2TF7wnKeAAAAAElFTkSuQmCC")}.jessibuca-container .jessibuca-loading{display:none;flex-direction:column;justify-content:center;align-items:center;position:absolute;z-index:20;left:0;top:0;right:0;bottom:0;width:100%;height:100%;pointer-events:none}.jessibuca-container .jessibuca-loading-text{line-height:20px;font-size:13px;color:#fff;margin-top:10px}.jessibuca-container .jessibuca-controls{background-color:#161616;box-sizing:border-box;display:flex;flex-direction:column;justify-content:flex-end;position:absolute;z-index:40;left:0;right:0;bottom:0;height:38px;padding-left:13px;padding-right:13px;font-size:14px;color:#fff;opacity:0;visibility:hidden;transition:all .2s ease-in-out;-webkit-user-select:none;user-select:none;transition:width .5s ease-in}.jessibuca-container .jessibuca-controls .jessibuca-controls-item{position:relative;display:flex;justify-content:center;padding:0 8px}.jessibuca-container .jessibuca-controls .jessibuca-controls-item:hover .icon-title-tips{visibility:visible;opacity:1}.jessibuca-container .jessibuca-controls .jessibuca-fullscreen,.jessibuca-container .jessibuca-controls .jessibuca-fullscreen-exit,.jessibuca-container .jessibuca-controls .jessibuca-icon-audio,.jessibuca-container .jessibuca-controls .jessibuca-microphone-close,.jessibuca-container .jessibuca-controls .jessibuca-pause,.jessibuca-container .jessibuca-controls .jessibuca-play,.jessibuca-container .jessibuca-controls .jessibuca-record,.jessibuca-container .jessibuca-controls .jessibuca-record-stop,.jessibuca-container .jessibuca-controls .jessibuca-screenshot{display:none}.jessibuca-container .jessibuca-controls .jessibuca-icon-audio,.jessibuca-container .jessibuca-controls .jessibuca-icon-mute{z-index:1}.jessibuca-container .jessibuca-controls .jessibuca-controls-bottom{display:flex;justify-content:space-between;height:100%}.jessibuca-container .jessibuca-controls .jessibuca-controls-bottom .jessibuca-controls-left,.jessibuca-container .jessibuca-controls .jessibuca-controls-bottom .jessibuca-controls-right{display:flex;align-items:center}.jessibuca-container.jessibuca-controls-show .jessibuca-controls{opacity:1;visibility:visible}.jessibuca-container.jessibuca-controls-show-auto-hide .jessibuca-controls{opacity:.8;visibility:visible;display:none}.jessibuca-container.jessibuca-hide-cursor *{cursor:none!important}.jessibuca-container .jessibuca-icon-loading{width:50px;height:50px;background:url("data:image/png;base64,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") no-repeat 50%;background-size:100% 100%;animation:rotation 1s linear infinite}.jessibuca-container .jessibuca-icon-screenshot{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAE5UlEQVRoQ+1YW2sdVRT+1s7JxbsoVkEUrIIX0ouz15zYNA+N1RdtQfCltlUfvLbqL/BCwZ8grbHtizQqPojgBSr0JkiMmT2nxgapqBURtPVCq7HxJCeZJVPmxDlzZubMmXOSEsnAvOy917fXt9e39tp7E5b4R0vcfywTuNgRbBgBx3HuJqLVzPzmYjprjHkcwAlmLqXNm4XAISLaSESPaq2HF4OE67rbRGRYRA7btn1fbgLGmKsA/Azg0gBkGzO/vZAkHMd5hIiqc5wHcCMz/5k0Z2oExsfHV1QqldPAf8lORNu11m8tBAljzFYAYWxRSl1vWdZvuQj4RsYYF4AVBlgIOVVlE55HRIxt23ZuCfmGjuOsJ6LPoiAistW27XfaEYmIbOYhPc9bXywWR1oiEJDYQkR1zrYjEjGyqfqbKd8a7kJVtLgQ+30i8pht2wfyRKIdmJkJBPkQTbILfudJ7CTZNBvVpggEcgpvc/ML38zESbLJsxBNE/A9biX0rdjGyTQXgbxyapdsarb0PMlXtWnGoXbKpm0Essqp3bJpK4E0OXmed3+hUBDP8w5FI91M0rdcyLLILElOCbaZilSWeXMncRx4klTCY1spfG3dhZJWx3GcDUR0EEB3ZMw0ET2gtT6SZWWzjmlrBIJCl0hAKfWgZVmHszqXZVxbCSxpCS2JJA6umIhe8ZKKVLPbaBJ+S9toqVRa53nedgAbAKwIwH4FcAzAa0R0l4i8F7PPz189k6RFRA+LyNcAXojDV0oNW5b1eW4Cxpg9AHZkSaaa6hhzb065uDSCH2LmRB8Sk9gY4293g43Qo/1pV80m8yQMfZSZ781cB1zXHRKRZ2IMpgD8A+DamL4ZItqitX4/jbQx5iEA7wLoihn3V/ACckWMJN/QWj9b1x5tGBsbW6uUOh5pPy0iL3Z2dn6ilJqanp5ep5TaJSLhF4NppdRNaU8gPmapVLrO87yfIoXuWyJ6uVKp+HmFjo6OQSJ6FcBtYT+UUmstyxqvkWuUgDFmP4AnQu2/e563qlgs+u9DNZ8xZhRAX7VRRPbath0XuXk7Y8xeAE+FgL6fnJzsHRwcLIfBR0ZGLunq6poAsDLUvp+Zw7b1r9PGmJMAbg8Z7WDmoThZuK67WkS+DD18fcPMdzSQUBR/EzN/nIC/SUQ+DPXV4dclsTHmHAD/SfHCNzc3t7Kvr++HJKeMMacA3BL0nyuXyzcPDAxMxo0fHR29slAo/Ajg6qD/fE9Pzw29vb1/x42fmJi4vFwu+5G/LOg/y8zXNJLQ2dAES5JANMQ7mfn1jBI6ycx3NiMhItqstf4oAX+ziHwQ6qvDj5NQNIn/ALCKmX+JSeIvABRD7fuY+ekGBPYBeDI05tTMzExvf3+/vz2Hk91/ET8RSeI6/DoCpVJpjed5fmKGvzMAXpqdnT3oed5Ud3d3v4jsAqBr9Ei0Rmv9VRqBBPzvROQVETnq2xJRdRu9tRF+bCVOKWT+Kvl/TSIFk6SW/LAjKfjV5K8rZABi8dOOEv7FI7Z8x6zwEWbemLbyMfJr5qiSiJ96oclymBOR3bZtP9+M89WxxpjdAHY2sN3DzM8ljWl4I3Nd9x7/OE1ENcdpETnmH3e11n41zv0l4J8RkU+J6AAz+xtF4teQQG7PFslwmcAiLfSyhC72Qv9/I/Avns2OT7QJskoAAAAASUVORK5CYII=") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-screenshot:hover{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAED0lEQVRoQ+2ZycsdRRTFf2ejqHFAMQqiYBTUoElUHLNx3GgCgpuYRF2o0UT9CxwQ/BMkMSbZSKLiQgQHUDCJgjiAxiEiESdEcJbEedgcKaj3UV+/6q7u/jovPPkK3qbr1ql76p5bt6qemPKmKfefeQKHOoLFCNg+H1gi6fFJOmv7VmCvpD1N87Yh8ApwNXCzpB2TIGF7DRDm2inpmt4EbB8LfAMcGUHWSHryYJKwfRMwmuMP4BRJv9TN2RgB2wuB72BWsq+V9MTBIGF7NZBiGzhJ0o+9CIRBtt8FLqgADC6nRDbpVO9Iuqi3hCKB5cDrGZDVkp4aIhIV2aSQyyW9MScCkcQqIOfsnCORkc3I31b5VtyFRmg1IQ7dt0ja3icSQ2C2JhAjUU2ykd+dE7tBNp2i2olAJJFuc+nCt564QTadF6IzgUhiVGiqyinKaQjZpJP2ItBXTkPJZhACXeU0pGwGI9BWTkPLZlACBTldG4o5EA6E1dY66edcyNrs8Q36zg1vVaTazNs7iXPgDVJJzYs7VRvHRzaDEohyugJ4CTi84sg/wHWSdnVxsGQ7aQLXS9pZcqpL/6AEplpCU5HE8YpJ9YrXUKQ6baN1+HPaRm1fBqwFQnKGK2ZoPwCvAo8Ai4FnMpPMHMwapHUj8DFwbw3+Dklv9iZgexOwvktSRduxU2VDlErwmyXV+lCbxLbDdndlCT3TX3vV7JgnKfRuSVflfMkSsL0ZuDMz4E/gL+CETN+/wCpJzzaRtn0D8DRwWMbu1/gCcnSm7zFJd1W/jxGwvQx4r2IYnlbuA14GAomQFw8B6YtBKFSnNj2BxEJ3IvB1pdB9CjwQ8yqYhcg/DJxZ8WOZpA/SbzkC24DbEqOfgPMkBRKzmu23gEuSj1sk5SI3Y2J7C3BHMuZz4FxJf6fgto8APgIWJd+3SUrHjr9O294HnJUMWi8pSGqs2V4CvJ88fH0i6eyChKr4KyS9WIO/Ang+6RvDz0XgABCeFEdtkaQv65yy/QVweuwPY0+T9FuNQ8cAXwHHxf7wdHiypN9r7BfEl8GjYv9+SceXJLQ/mSDYTh2Baog3SHq0pYT2STqno4RWSnqhBn8l8FzSN4bfJol/jkn8bXUS228DFyfft0paVyCwFbg9sQkSDEkctueZZju8iO+tJPEYfo7A0piYKd73wP3xnB+20cvjNnphxdmlkj4sEMjhfwY8COyOY0fb6Bkl/K6FLKxS+M1KpDhJY8mvrG5doRwlf66QZfGbjhLh4pEt35kV3iUp/IvTunU8qtTil/7gaHOY2yjpntaez9b5RmBDYewmSXfX2RRvZLYvbThOh+NuqMa9Ww1+yLnXgO2SwkZR24oEens2oYHzBCa00PMSOtQL/f+NwH+Hg8hAnbrYgQAAAABJRU5ErkJggg==") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-play{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAACgklEQVRoQ+3ZPYsTQRjA8eeZZCFlWttAwCIkZOaZJt8hlvkeHrlccuAFT6wEG0FQOeQQLCIWih6chQgKgkkKIyqKCVYip54IWmiQkTmyYhFvd3Zn3yDb7szu/7cv7GaDkPEFM94PK0DSZ9DzDAyHw7uI2HRDlVJX5/N5r9FoHCYdr/fvCRiNRmpJ6AEidoUQ15NG+AH8BgD2n9AHANAmohdJQfwAfgGA4xF4bjabnW21Whob62ILoKNfAsAGEd2PU2ATcNSNiDf0/cE5/xAHxDpgEf0NADaJ6HLUiKgAbvcjpdSGlPJZVJCoAUfdSqkLxWLxTLlc/mkbEgtgET1TSnWklLdtIuIEuN23crlcp16vv7cBSQKgu38AwBYRXQyLSArg3hsjRDxNRE+CQhIF/BN9qVAobFYqle+mkLQAdLd+8K0T0U0TRJoAbvc9fVkJId75gaQRoLv1C2STiPTb7rFLWgE6+g0RncwyYEJEtawCvjDGmpzzp5kD6NfxfD7frtVqB17xen2a7oG3ALBm+oMoFQBEPD+dTvtBfpImDXjIGFvjnD/3c7ksG5MU4HDxWeZa0HB3XhKAXcdxOn5vUi9gnIDXSqm2lHLPK8pkfVyAbSLqm4T5HRs1YB8RO0KIid8g03FRAT4rpbpSyh3TINPxUQB2GGM9zvkn05gg420CJovLZT9ISNA5tgB9ItoOGhFmnh/AcZ/X9xhj65zzV2Eiwsz1A1j2B8dHAOgS0W6YnduY6wkYj8d3lFKn/j66Ea84jtOrVqtfbQSE3YYnYDAY5Eql0hYAnNDv6kKIx2F3anO+J8DmzqLY1goQxVE12ebqDJgcrSjGrs5AFEfVZJt/AF0m+jHzUTtnAAAAAElFTkSuQmCC") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-play:hover{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAACEElEQVRoQ+2ZXStEQRjH/3/yIXwDdz7J+i7kvdisXCk3SiFJW27kglBcSFFKbqwQSa4krykuKB09Naf2Yndn5jgzc06d53Znd36/mWfeniVyHsw5PwqB0DOonYEoijYBlOpAFwCMkHwLDS/9mwhEDUCfAAyTXA4tYSLwC6CtCegegH6S56FETAR+AHRoACcBTJAUWa+RloBAXwAYIrnt0yBNgZi7qtbHgw8RFwLC/QFglOScawlXAjH3gUqrE1cirgVi7mkAYyS/0xbxJSDcdwAGSa6nKeFTIOZeUyL3aYiEEBDuLwDjJGf+KxFKIOY+BdBL8iipSGiBmHtWbbuftiJZERBuOfgGSK7aSGRJIObeUml1ayKSRQHhlgtkiaTcdltGVgUE+ppkV54FaiS78yrwqlLoOI8Cch2XV548W7WRpTVwA6DP9kGUFYEpAOUkT9LQAvtq1M+0udKkQSgBqSlJWWYxKXj8vRACK+o6bbRIdYI+Ba7U7rKjg7L53JdAhWTZBsy0rWuBXZUuNVMg23auBF7UIl2yBbJt70JAoKV6/WwLk6R9mgKSJlJ1kLTxFmkJyCla8UZd15GJQKvyumyJ8gy8DAEvfZoINPqD41EtUjmUgoaJwAaAnjrKebVI34OSq85NBNqlCAWgE0CV5GEWwI3vQlmCbcSinYFCwPEIFDPgeIC1P1/MgHaIHDf4Aydx2TF7wnKeAAAAAElFTkSuQmCC") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-pause{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABA0lEQVRoQ+1YwQqCUBAcfWXXsLr2AXWTPXno8yVB8AP6Aa3oHI+kCDqYaawJljSe133uzO44bx0M/HEG/v1gAd9mkAyQgY4I/F8LJUlyrQFtD2AtIkcNoFEU+Z7n7QD4DfFHEVlocrVmgAUAIAOl3mILPcDgEFcUhyrUKMGUUcroc3NQRimj9XJBGaWMvvPydKN0o6/9QTdKN6rZANxj6EbpRulGuZnjYqs8BbyR8Ub2Izeys+u6yyAIDpo/ehzHM2NMDsA0xFsRmWhyfTIDWSXxCEBmrd2EYXjSHJqm6bQoii2AOYBL5Z0xgFxEVppcrQvQJO0zhgX0iXbdWWSADHRE4AZQ731AhEUeNwAAAABJRU5ErkJggg==") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-pause:hover{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAA7klEQVRoQ+2YSwrCQBBEX6HiVvxsPYDewfN7By/gD9ciQkvERQwJdBSiYs0mEDo96aruombEjy/9+P/jAj7NoBkwA28i8H8tFBFRA9oeWEo6ZgCNiDGwAYpn3TpKmmVytWbABQBmoNRbbqEHGB7iiuJYhRol2DJqGX1uDsuoZdRmLuNZSzGWUcuoZdRHSp/IylNgK2ErYSthK3FHwLcSvpXIjoLt9Jfa6TMwl3TIMBkRE2AH9BriL5KGmVyvWIltJXEfKN6tJJ0ym0bECFgDU+Ba+WZQFCdpkcnVuoBM0i5jXECXaNftZQbMwJsI3AAPN3dAQflHegAAAABJRU5ErkJggg==") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-record{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAC+UlEQVRoQ+1ZS2sTURT+zlDJYE3XSq219QHVuEjnJDT+Bff9Abqw2voAEfGxqygUqWhVFHGl/yMLu9BwByxk5SNI66ML6U7axjhHbmhgWiftncxoOiV3FcI53z3f/e65594zhIQPSnj86BBot4IdBToKRFyBnbeFlFIScVEiuYvIWC6Xe2YK8pcC7SYA4CMzH4mDQBXAqilQBDsLQLfPf9FxnF4i8kwwmypARI+Wl5dvmIBEsUmlUkNE9NaHsVCpVAZGR0d/m+A2JSAid3K53E0TkCg2pVKpz7KseR/GfKVSGYxMAMA0M1+JEpyJb6lUOm5ZVnkrAsVisaunp+esiByr1Wp3R0ZGvmifzZK4XQQWHMc52MgBpdQuAOcAXABwuB400ZTjONdaIjA7O5u2bVsnWU1EujzP+5nP5xdMVjvIJkCBD8x8VCm1G8AYgAkAAxt8Z5j5YmgCSqlTAJ4D2OcD/AXgATNfbYVEAIFPIvKKiE4D6GuCea8xX6gtpJT6DmBvECgRFRzHeROWRAABE4iWCbwHEFhkPM/L5vP5dyaz+23+KwHXdR3P854S0YG1ILSCuthNMfNM2OC1/RYENLY+ygcBnPfht6ZAA6BYLNr6dyqVokKhsGpaNQ2TWJstreXaE2aed133sojcj41AKyvdzCdAgSXLsk4MDw9/a/i4rntbRPxFNZoC/5jAV2be759DKTUJ4FZSFFi0bbs/k8noy2R9dAjEuWU2YgXkQOK3kD6BMsysi2Z9JC2Jdcw/ALzwPO+xvmcl7Rj177JVEbkO4BARjSflFDJJuW1dBxJPoCIiL4noDIB1BS0pW6j+oJmbm+uuVqvjRKQfLr0bZHnIzJf0f6HeAybahrUJqAPruhLlcnnPysqKfpXp11n/Gv62zoHAroS+AafT6QkiGrIsazKbzX7eVIHEt1US39gCkOzWYthkjNE+tuZujDGZQ8XRXn8N4KT5lLFZ6uaYPt+nwyDuvC80YdhvB9uOAu1WoaNAR4GIK/AHvdr+QAexB7EAAAAASUVORK5CYII=") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-record:hover{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAACfUlEQVRoQ+2ZSYsUQRCFvycK4nJXXEbHBdwO4kn/gv9CD467ICIutxEFkREdFUU86T/xojcPntyQcT2INw+uISFVkD1Wd2dWlU7nUHlqisiX+fJFZGREi8yHMt8/HYG5VrBToFOg4QnMPxcyM2t4KE2nT0i6EwvylwIjQOCFpE1tEPgGfI0FamC3AFgazP8IrJL0KwZzkAI3gLMxIA1ttgCPA4w3wHpJP2NwBxG4KOlcDEgTGzNbA8wEGP57vA0CU5JONtlczFwz2wY8HUbAzBYCB4CtwCVJb33OIAXmioC70LoyBsxsEXAQOApsLIhelnS6FgEzW+5BBvwA/FS+SPJFa40KBZ5L2mxmS4AJ4IjHxCzwaUnHkgmY2V7gLrAyAPwOXJN0qg6DCgIvgQfAPsDjo2pcKddLciEz+wCs6AO6W9KjVBIVBGIgahN4BvRLMjslPYlZPbT53wR2AbeBtcUmXEFPdh5U06mbd/shBBzbr/Jx4FCAX0+BEsDMFocEYrNmFcE+BD4XsXZL0oyZnQCutkagzkn3m1NBwDe/Q9L74MAuFEqUn5op8I8JvJO0elacTALnc1HAH3Njkvwx+WeYWUegTa/pwaqIgexdyIN4uyRPmqULZRXEvulPwD3gpr+zcrtGQxfzRHYG2AAczuUWiom3kc4D2RN4BdwH9gM9CS0XFyoLGu9UuN974eIFVDiuSzruH5LqgRhtU20q8kBPV8LMlhVVmVdnYwX+SMdAZVeieAF7eeltmElJr4cpkH1bJfvGVvatxdR4bMu+teZuWxtKxWncXn8I7EldtQV7vz79fp9KwZp//9CksB8F206BuVahU6BToOEJ/Ab7+KdABdTt8AAAAABJRU5ErkJggg==") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-recordStop{background:url("data:image/png;base64,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") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-recordStop:hover{background:url("data:image/png;base64,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") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-fullscreen{background:url("data:image/png;base64,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") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-fullscreen:hover{background:url("data:image/png;base64,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") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-fullscreenExit{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAADd0lEQVRoQ+2Zz2sdVRTHv+fJBDW6anDVXen6wZszYxYBiYgtFGst3VSDunKjpS0GpUlqfjVpsVVs6aaL0or4YxMVFCJZ2ZLdPUP+gq5bQnTxtNAkfTnlhnnlkmQy9yV9780rudt77tzv5/y4v4bQ4Y06XD/2ANodwec/AiJygJnvtdvTWfPnRkBEJAiCN8rl8kMfiPn5+Ve7u7v3rays0Orq6lJfX99/PuN2auMDoAD+BvA2M6/mTWSMOUtE48D6AjHGzN/kjdlNvy+AnWOOmQ/lTSYiEwDOWzsimgrDcCRvzG76GwGw8/zJzO9sN6GInAMwbW1UdSSKoqndCMwb6wNwGsB39Q+p6h/M/C4R2dTa1AoHYBWKyCkA1+pqiWi2Wq0e7e/vf7yRoJAAKcQggMtuJKIoOtoxACnE0/xOi/SXMAxPuhCFjUBdpIjYVWXSEf0TM3/g9BeriDMKdSPEz8z8vrU1xgwT0YXCrEJZy1iSJKOqOub0/8jMA0mSfKKqNwoPkHp7ioiGHIhRIvpHVa93BEBa2JcAfOlALAHo6RgAKzRJkk9V1S6xL7kpV4idOM31taxaIKJHqmpPnMMA9hcOQES2PDJkAT1XAAC+ZebPfWB3auNzmLObVsNRUNUXVHUujuM7OxXnMy4XwOcj29mIyOuq+lapVGrYCelKpkEQ3CyXy4tbzdN0AGPMxr2iYZ+sra3FcRybtgCIiK2BKw2rdgaUSqWoUqlIkQAepFDdAF7cBq5ERI9rtdr1OI7tmE2t6SmUEYFHAEaexYW/1QC2EF+ru5GIvg7D0D2GNJxprQY4o6qv1I/b6SpzOYqiLxpWng5oOQAzXxWRWwA+dkRfYOb1p5hGW6sBJpn5KytSRG4D+KguWFXHoyhy7xdeLC0F2ChSRL4H8OFuINoKYIUbY34gogHH3eeZef1K6tPaDpCm068A3nMEDzHzxY4BUNWSiPxORO6z5aDPPlGICNQ9bYyZIaLjjudzIQoFkKbTbwCO+UI0HcB9J/LdeY0xs0R02IGYYObRrWqiFQCfEZEtSHsfmGZm+4qxbbM/hQD8BeBNa0hEM2EYnmgLgP3lFARBT1dXly4vL//b29tbzQNIU+llAHeJaLFSqRzJes5vegR8xGbZLCwsHKzVav8z8/0sm0ID+MDvAfh4qZk2exFopnd9vv0ELrXBQO7fD10AAAAASUVORK5CYII=") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-fullscreenExit:hover{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAC/ElEQVRoQ+2Zy49NQRCHvx+ReK6IlZ34E7CUiCAR4xEbTLCyQRATYswwb2IQZDYWgojHZpCQECts+ResiQwLj0RClNSkb9Lu3HtPz7mZc8+V6eXt6tP1VVV3VdcVbT7U5vozC9BqD/7/HjCzlZLet9rS9fbP9ICZvQPWSfqRAmFmS4ClMHm+JiR9S1mXVyYFwIBXwEZJv7I2MrPjQH8A6JN0OWtNM/OpAL7HS0mbsjYzswGgN8gNS+rJWtPM/HQAfJ9nkrY22tDMTgMjQaZH0nAzCmatTQE4ClyNPvQU2CbJQ2vKKB2Aa2hmR4DrkbbPgQ5Jv6sJSgkQILqA0dgTkjraBiBAxPHtPz2UtDuGKK0HKkqamd8qg5HS9yXtjebLdYjrHNRqiAeS9gQvnQGGSnML1bvGzOwc0BfN35PUaWYHgRulBwjW9ju+O4JwqM/AWFsABIgLwKkIYgJY1jYAAeJQuGIXVIVcKTKxh8WfBin9J+AVpx/eFWUEqFkyNACKp0rhgWYArkg6kQibSyylmPOklQdibijBX+fSLHFRJkDid+qKmdlaYENOI0zeEcBNSZ9qbVIEQHWuyGOTNZLetgrAz8ClPFpHa1ZL8rf5lFGEB2oBfAxQi4D5DeDmAP7mGJPka0oD4LnDr9imH/xFe8AP4vLIjBclxWXItCOtaIBjwOKo3HaFRyWdnLbmYUHhAJKumdkt4ECk9JCkSitmWixFAwxKOjt5uZvdBvZH2vZLit8XSSBFA/yjpJndAfY1A9FSgOCJu0BnBNErqfIkzfRCywECxCNgR6Rtt6TzmdqHBmyKXG4ZM4sTWc04NzNPWE+AuG3ZlZInSuGBinXMbBzYGVkrE6JUACGcHgPbUyGKAIj7REmZ18y897o5ghiQ5E/bltRChwE/kF7Xj0jyLkbDYWbzgBfA+iA4LmlXqwD8LydvszjAF0lfswBCKC0E3gBeP22p186f8RBKUbaejJmtAr5L+lBPptQAKfCzAClWmkmZWQ/MpHVTvv0X9iFAQGQyevIAAAAASUVORK5CYII=") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-audio{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAACrUlEQVRoQ+2ZPYgTURCAZzbBXJnCeL2Cnb87b9MEtPBUrrMQFAtrtT5/ClGs9LBWWz0RtbBUFCF4oJDsbO68wsLA2YqQSmLlvpEHu7IuMdlLcus+yUKKhJfZ+ebnvZl5CJY/aLn+MAP41x7M1QPMfFtr/crzvHfTAs8FoNPp1LTWzwHgqIg0lFLvrQHwfX8BER8DwC6jNCIecF13wwoA3/dvIuKNpLJa60Oe560XGoCZd4rICiKeTCtaeABmPg4AJmRqg6xcaABmvg4At4aFRyEBhoVM4UMoCplHADCfJTEL5YEsIVNID5iQAYCHALCYxeq5b6PMfF5EBAAEESthGK7W6/XPRpFWq7W3VCqtZg2ZcT3g+/6i4zjzIlLSWn/yPO/DIGMNLCWY2Sj/+xGRK0qpZfNDEASnROTFVi0fr8+aA8z8Ld6KEfGt67oLYwMAwEUium8EREn7OgeAjwCwPyo/nrque3YSgAtE9GDaAM1mc65arc4Zuf1+P2w0Gt9jJZl5DQAORt+fENG5wgEw8zUAMB/zbBBRwyqAIAjuiMjlSOlNItpjFUCqWl0josMzgChR/9hGAWBbknjmAdPhDdqa0gfZzAMJKyVP4v8hhJYRcSni+0JEu63ahZj5anyQici6UuqIVQDdbrfS6/UqRulyufyTiH5sF8AlIro37VpoWEHIzGZ2tM+sEZFnSqkzk9RCS0R01wjIsZz+mug53hDRia0AnI4bGgDYISItz/M2jYC8Gpp2u30MEWuO4zha665Sqp0ZYFStX/iWchRAItFGzoHSsrJ2ZFl1mHg6bfVYJeGJv85CC++BpIJZ5kSFC6G0ha0e7mYJqcJ7IOkRay84UhD2XjHFIFZf8iW9YcYoYRi+tO6aNeupOs66iU/icV46zf/MAKZpzXFk/QL+JG1PUPhRiQAAAABJRU5ErkJggg==") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-audio:hover{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAACSElEQVRoQ+2Zu4sUQRCHf5+C+gf4yBXMfMYHGvjCzEBQDIzV+HwEohipGKupD0QNDE8UEwUFTe68wEDhTMVUMFJ+0tArzbjs9u3Ojt0wBR0M9MzUV1XdXVWNKhcq1189wP/2YKcesH1d0nPgdVvgnQDY3iTpqaT9kuaAt9UA2D4o6aGkzVHpXcByFQC2r0q60lB2D7BUNIDtjZIeSDoyRNGyAWwfiiET4n6YlAtg+7Kka2PCozyAMSHT5CkLIIbMfUlbMhdmOQCZIVOeB2LI3JN0NNPq6bTZe8D2aUmOY72kN8DnoIXt7eF5FSEzkQdsB+OEsFwr6RPwbpixhqYStoPyqVwAbkaAY5KeTWD5wStZHrD9XdJgK34FhBP9H8kFOAvciQBhn3/RAcBHSTvjfx4DJ6cBOAPcbRvA9gZJYQT5DfwYKGl7UdLu+PwIOFUiwCVJYQRZBuZqA7gh6XxUegXYVhtAmq0uAnt7gLhQm9vorBZx74Hcc6D3QLKH/z2JGyVnlYs4pCfzEe4rsLW2XehicpAtAftqAwiZbhhBfgE/ZwVwDrjddi40KiG0HXpHO+KcJ8CJaXKheeBWBOgqnf6W1BwvgcOrATieFDTrJL0HViJAVwXNgVgPrJH0BfiQDTDKtREiNK7KLSnHASQLLacP1PxcVkWWq8PU3emq2yqJJ0b1Qsv2QKpdZp+orBBqmrfq5m5mSJXtgUZI1XnB0YCo94opCal6L/ka3ghtlIXqrllzT9VJ5k19Ek/y0zbf6QHatOYk3/oDujC8QMWgjf4AAAAASUVORK5CYII=") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-mute{background:url("data:image/png;base64,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") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-mute:hover{background:url("data:image/png;base64,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") no-repeat 50%;background-size:100% 100%}.jessibuca-container .jessibuca-icon-text{font-size:14px;width:30px}.jessibuca-container .jessibuca-speed{font-size:14px;color:#fff}.jessibuca-container .jessibuca-quality-menu-list{position:absolute;left:50%;bottom:100%;visibility:hidden;opacity:0;transform:translateX(-50%);transition:visibility .3s,opacity .3s;background-color:rgba(0,0,0,.5);border-radius:4px}.jessibuca-container .jessibuca-quality-menu-list.jessibuca-quality-menu-shown{visibility:visible;opacity:1}.jessibuca-container .icon-title-tips{pointer-events:none;position:absolute;left:50%;bottom:100%;visibility:hidden;opacity:0;transform:translateX(-50%);transition:visibility .3s ease 0s,opacity .3s ease 0s;background-color:rgba(0,0,0,.5);border-radius:4px}.jessibuca-container .icon-title{display:inline-block;padding:5px 10px;font-size:12px;white-space:nowrap;color:#fff}.jessibuca-container .jessibuca-quality-menu{padding:8px 0}.jessibuca-container .jessibuca-quality-menu-item{display:block;height:25px;margin:0;padding:0 10px;cursor:pointer;font-size:14px;text-align:center;width:50px;color:hsla(0,0%,100%,.5);transition:color .3s,background-color .3s}.jessibuca-container .jessibuca-quality-menu-item:hover{background-color:hsla(0,0%,100%,.2)}.jessibuca-container .jessibuca-quality-menu-item:focus{outline:none}.jessibuca-container .jessibuca-quality-menu-item.jessibuca-quality-menu-item-active{color:#2298fc}.jessibuca-container .jessibuca-volume-panel-wrap{position:absolute;left:50%;bottom:100%;visibility:hidden;opacity:0;transform:translateX(-50%) translateY(22%);transition:visibility .3s,opacity .3s;background-color:rgba(0,0,0,.5);border-radius:4px;height:120px;width:50px;overflow:hidden}.jessibuca-container .jessibuca-volume-panel-wrap.jessibuca-volume-panel-wrap-show{visibility:visible;opacity:1}.jessibuca-container .jessibuca-volume-panel{cursor:pointer;position:absolute;top:21px;height:60px;width:50px;overflow:hidden}.jessibuca-container .jessibuca-volume-panel-text{position:absolute;left:0;top:0;width:50px;height:20px;line-height:20px;text-align:center;color:#fff;font-size:12px}.jessibuca-container .jessibuca-volume-panel-handle{position:absolute;top:48px;left:50%;width:12px;height:12px;border-radius:12px;margin-left:-6px;background:#fff}.jessibuca-container .jessibuca-volume-panel-handle:before{bottom:-54px;background:#fff}.jessibuca-container .jessibuca-volume-panel-handle:after{bottom:6px;background:hsla(0,0%,100%,.2)}.jessibuca-container .jessibuca-volume-panel-handle:after,.jessibuca-container .jessibuca-volume-panel-handle:before{content:"";position:absolute;display:block;left:50%;width:3px;margin-left:-1px;height:60px}.jessibuca-container.jessibuca-fullscreen-web .jessibuca-controls{width:100vh;transform:translateX(-13vw) translateY(-47.8vh) rotate(270deg);transition:width .5s ease-in}.jessibuca-container.jessibuca-fullscreen-web .jessibuca-play-big:after{transform:rotate(270deg)}.jessibuca-container.jessibuca-fullscreen-web .jessibuca-loading{flex-direction:row}.jessibuca-container.jessibuca-fullscreen-web .jessibuca-loading-text{transform:rotate(270deg)}');class He{constructor(e){var t;this.player=e,((e,t)=>{e._opt.hasControl&&e._opt.controlAutoHide?e.$container.classList.add("jessibuca-controls-show-auto-hide"):e.$container.classList.add("jessibuca-controls-show");const i=e._opt,o=i.operateBtns;e.$container.insertAdjacentHTML("beforeend",`\n ${i.background?`
`:""}\n
\n ${Pe.loading}\n ${i.loadingText?`
${i.loadingText}
`:""}\n
\n ${i.hasControl&&o.play?'
':""}\n ${i.hasControl?`\n
\n
\n
\n ${i.showBandwidth?'
':""}\n
\n
\n ${o.audio?`\n
\n ${Pe.audio}\n ${Pe.mute}\n
\n
\n
\n
\n
\n
\n
\n `:""}\n ${o.play?`
${Pe.play}
${Pe.pause}
`:""}\n ${o.screenshot?`
${Pe.screenshot}
`:""}\n ${o.record?`
${Pe.record}
${Pe.recordStop}
`:""}\n ${o.fullscreen?`
${Pe.fullscreen}
${Pe.fullscreenExit}
`:""}\n
\n
\n
\n `:""}\n\n `),Object.defineProperty(t,"$poster",{value:e.$container.querySelector(".jessibuca-poster")}),Object.defineProperty(t,"$loading",{value:e.$container.querySelector(".jessibuca-loading")}),Object.defineProperty(t,"$play",{value:e.$container.querySelector(".jessibuca-play")}),Object.defineProperty(t,"$playBig",{value:e.$container.querySelector(".jessibuca-play-big")}),Object.defineProperty(t,"$pause",{value:e.$container.querySelector(".jessibuca-pause")}),Object.defineProperty(t,"$controls",{value:e.$container.querySelector(".jessibuca-controls")}),Object.defineProperty(t,"$fullscreen",{value:e.$container.querySelector(".jessibuca-fullscreen")}),Object.defineProperty(t,"$fullscreen",{value:e.$container.querySelector(".jessibuca-fullscreen")}),Object.defineProperty(t,"$volume",{value:e.$container.querySelector(".jessibuca-volume")}),Object.defineProperty(t,"$volumePanelWrap",{value:e.$container.querySelector(".jessibuca-volume-panel-wrap")}),Object.defineProperty(t,"$volumePanelText",{value:e.$container.querySelector(".jessibuca-volume-panel-text")}),Object.defineProperty(t,"$volumePanel",{value:e.$container.querySelector(".jessibuca-volume-panel")}),Object.defineProperty(t,"$volumeHandle",{value:e.$container.querySelector(".jessibuca-volume-panel-handle")}),Object.defineProperty(t,"$volumeOn",{value:e.$container.querySelector(".jessibuca-icon-audio")}),Object.defineProperty(t,"$volumeOff",{value:e.$container.querySelector(".jessibuca-icon-mute")}),Object.defineProperty(t,"$fullscreen",{value:e.$container.querySelector(".jessibuca-fullscreen")}),Object.defineProperty(t,"$fullscreenExit",{value:e.$container.querySelector(".jessibuca-fullscreen-exit")}),Object.defineProperty(t,"$record",{value:e.$container.querySelector(".jessibuca-record")}),Object.defineProperty(t,"$recordStop",{value:e.$container.querySelector(".jessibuca-record-stop")}),Object.defineProperty(t,"$screenshot",{value:e.$container.querySelector(".jessibuca-screenshot")}),Object.defineProperty(t,"$speed",{value:e.$container.querySelector(".jessibuca-speed")})})(e,this),ze(e,this),t=this,Object.defineProperty(t,"controlsRect",{get:()=>t.$controls.getBoundingClientRect()}),((e,t)=>{const{events:{proxy:i},debug:o}=e;function r(e){const{bottom:i,height:o}=t.$volumePanel.getBoundingClientRect(),{height:r}=t.$volumeHandle.getBoundingClientRect();return pe(i-e.y-r/2,0,o-r/2)/(o-r)}i(window,["click","contextmenu"],(i=>{i.composedPath().indexOf(e.$container)>-1?t.isFocus=!0:t.isFocus=!1})),i(window,"orientationchange",(()=>{setTimeout((()=>{e.resize()}),300)})),i(t.$controls,"click",(e=>{e.stopPropagation()})),i(t.$pause,"click",(t=>{e.pause()})),i(t.$play,"click",(t=>{e.play()})),i(t.$playBig,"click",(t=>{e.play()})),i(t.$volume,"mouseover",(()=>{t.$volumePanelWrap.classList.add("jessibuca-volume-panel-wrap-show")})),i(t.$volume,"mouseout",(()=>{t.$volumePanelWrap.classList.remove("jessibuca-volume-panel-wrap-show")})),i(t.$volumeOn,"click",(i=>{i.stopPropagation(),me(t.$volumeOn,"display","none"),me(t.$volumeOff,"display","block"),e.lastVolume=e.volume,e.volume=0})),i(t.$volumeOff,"click",(i=>{i.stopPropagation(),me(t.$volumeOn,"display","block"),me(t.$volumeOff,"display","none"),e.volume=e.lastVolume||.5})),i(t.$screenshot,"click",(t=>{t.stopPropagation(),e.video.screenshot()})),i(t.$volumePanel,"click",(t=>{t.stopPropagation(),e.volume=r(t)})),i(t.$volumeHandle,"mousedown",(()=>{t.isVolumeDroging=!0})),i(t.$volumeHandle,"mousemove",(i=>{t.isVolumeDroging&&(e.volume=r(i))})),i(document,"mouseup",(()=>{t.isVolumeDroging&&(t.isVolumeDroging=!1)})),i(t.$record,"click",(t=>{t.stopPropagation(),e.recording=!0})),i(t.$recordStop,"click",(t=>{t.stopPropagation(),e.recording=!1})),i(t.$fullscreen,"click",(t=>{t.stopPropagation(),e.fullscreen=!0})),i(t.$fullscreenExit,"click",(t=>{t.stopPropagation(),e.fullscreen=!1})),e._opt.hasControl&&e._opt.controlAutoHide&&(i(e.$container,"mouseover",(()=>{e.fullscreen||me(t.$controls,"display","block")})),i(e.$container,"mouseout",(()=>{me(t.$controls,"display","none")})))})(e,this),e._opt.hotKey&&((e,t)=>{const{events:{proxy:i}}=e,o={};function r(e,t){o[e]?o[e].push(t):o[e]=[t]}r(re,(()=>{e.fullscreen&&(e.fullscreen=!1)})),r(Ae,(()=>{e.volume+=.05})),r(se,(()=>{e.volume-=.05})),i(window,"keydown",(e=>{if(t.isFocus){const t=document.activeElement.tagName.toUpperCase(),i=document.activeElement.getAttribute("contenteditable");if("INPUT"!==t&&"TEXTAREA"!==t&&""!==i&&"true"!==i){const t=o[e.keyCode];t&&(e.preventDefault(),t.forEach((e=>e())))}}}))})(e,this),this.player.debug.log("Control","init")}destroy(){this.$poster&&this.player.$container.removeChild(this.$poster),this.$loading&&this.player.$container.removeChild(this.$loading),this.$controls&&this.player.$container.removeChild(this.$controls),this.$playBig&&this.player.$container.removeChild(this.$playBig),this.player.debug.log("control","destroy")}autoSize(){const e=this.player;e.$container.style.padding="0 0";const t=e.width,i=e.height,o=t/i,r=e.video.$videoElement.width/e.video.$videoElement.height;if(o>r){const o=(t-i*r)/2;e.$container.style.padding=`0 ${o}px`}else{const o=(i-t/r)/2;e.$container.style.padding=`${o}px 0`}}}Ye(".jessibuca-container{position:relative;width:100%;height:100%;overflow:hidden}.jessibuca-container.jessibuca-fullscreen-web{position:fixed;z-index:9999;left:0;top:0;right:0;bottom:0;width:100vw!important;height:100vh!important;background:#000}");class Xe{static init(){Xe.types={avc1:[],avcC:[],hvc1:[],hvcC:[],btrt:[],dinf:[],dref:[],esds:[],ftyp:[],hdlr:[],mdat:[],mdhd:[],mdia:[],mfhd:[],minf:[],moof:[],moov:[],mp4a:[],mvex:[],mvhd:[],sdtp:[],stbl:[],stco:[],stsc:[],stsd:[],stsz:[],stts:[],tfdt:[],tfhd:[],traf:[],trak:[],trun:[],trex:[],tkhd:[],vmhd:[],smhd:[]};for(let e in Xe.types)Xe.types.hasOwnProperty(e)&&(Xe.types[e]=[e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2),e.charCodeAt(3)]);let e=Xe.constants={};e.FTYP=new Uint8Array([105,115,111,109,0,0,0,1,105,115,111,109,97,118,99,49]),e.STSD_PREFIX=new Uint8Array([0,0,0,0,0,0,0,1]),e.STTS=new Uint8Array([0,0,0,0,0,0,0,0]),e.STSC=e.STCO=e.STTS,e.STSZ=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),e.HDLR_VIDEO=new Uint8Array([0,0,0,0,0,0,0,0,118,105,100,101,0,0,0,0,0,0,0,0,0,0,0,0,86,105,100,101,111,72,97,110,100,108,101,114,0]),e.HDLR_AUDIO=new Uint8Array([0,0,0,0,0,0,0,0,115,111,117,110,0,0,0,0,0,0,0,0,0,0,0,0,83,111,117,110,100,72,97,110,100,108,101,114,0]),e.DREF=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),e.SMHD=new Uint8Array([0,0,0,0,0,0,0,0]),e.VMHD=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0])}static box(e){let t=8,i=null,o=Array.prototype.slice.call(arguments,1),r=o.length;for(let e=0;e>>24&255,i[1]=t>>>16&255,i[2]=t>>>8&255,i[3]=255&t,i.set(e,4);let A=8;for(let e=0;e>>24&255,e>>>16&255,e>>>8&255,255&e,t>>>24&255,t>>>16&255,t>>>8&255,255&t,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]))}static trak(e){return Xe.box(Xe.types.trak,Xe.tkhd(e),Xe.mdia(e))}static tkhd(e){let t=e.id,i=e.duration,o=e.presentWidth,r=e.presentHeight;return Xe.box(Xe.types.tkhd,new Uint8Array([0,0,0,7,0,0,0,0,0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t,0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,o>>>8&255,255&o,0,0,r>>>8&255,255&r,0,0]))}static mdia(e){return Xe.box(Xe.types.mdia,Xe.mdhd(e),Xe.hdlr(e),Xe.minf(e))}static mdhd(e){let t=e.timescale,i=e.duration;return Xe.box(Xe.types.mdhd,new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t,i>>>24&255,i>>>16&255,i>>>8&255,255&i,85,196,0,0]))}static hdlr(e){let t=null;return t="audio"===e.type?Xe.constants.HDLR_AUDIO:Xe.constants.HDLR_VIDEO,Xe.box(Xe.types.hdlr,t)}static minf(e){let t=null;return t="audio"===e.type?Xe.box(Xe.types.smhd,Xe.constants.SMHD):Xe.box(Xe.types.vmhd,Xe.constants.VMHD),Xe.box(Xe.types.minf,t,Xe.dinf(),Xe.stbl(e))}static dinf(){return Xe.box(Xe.types.dinf,Xe.box(Xe.types.dref,Xe.constants.DREF))}static stbl(e){return Xe.box(Xe.types.stbl,Xe.stsd(e),Xe.box(Xe.types.stts,Xe.constants.STTS),Xe.box(Xe.types.stsc,Xe.constants.STSC),Xe.box(Xe.types.stsz,Xe.constants.STSZ),Xe.box(Xe.types.stco,Xe.constants.STCO))}static stsd(e){return"audio"===e.type?Xe.box(Xe.types.stsd,Xe.constants.STSD_PREFIX,Xe.mp4a(e)):"avc"===e.videoType?Xe.box(Xe.types.stsd,Xe.constants.STSD_PREFIX,Xe.avc1(e)):Xe.box(Xe.types.stsd,Xe.constants.STSD_PREFIX,Xe.hvc1(e))}static mp4a(e){let t=e.channelCount,i=e.audioSampleRate,o=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,t,0,16,0,0,0,0,i>>>8&255,255&i,0,0]);return Xe.box(Xe.types.mp4a,o,Xe.esds(e))}static esds(e){let t=e.config||[],i=t.length,o=new Uint8Array([0,0,0,0,3,23+i,0,1,0,4,15+i,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([i]).concat(t).concat([6,1,2]));return Xe.box(Xe.types.esds,o)}static avc1(e){let t=e.avcc;const i=e.codecWidth,o=e.codecHeight;let r=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,i>>>8&255,255&i,o>>>8&255,255&o,0,72,0,0,0,72,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,255,255]);return Xe.box(Xe.types.avc1,r,Xe.box(Xe.types.avcC,t))}static hvc1(e){let t=e.avcc;const i=e.codecWidth,o=e.codecHeight;let r=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,i>>>8&255,255&i,o>>>8&255,255&o,0,72,0,0,0,72,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,255,255]);return Xe.box(Xe.types.hvc1,r,Xe.box(Xe.types.hvcC,t))}static mvex(e){return Xe.box(Xe.types.mvex,Xe.trex(e))}static trex(e){let t=e.id,i=new Uint8Array([0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]);return Xe.box(Xe.types.trex,i)}static moof(e,t){return Xe.box(Xe.types.moof,Xe.mfhd(e.sequenceNumber),Xe.traf(e,t))}static mfhd(e){let t=new Uint8Array([0,0,0,0,e>>>24&255,e>>>16&255,e>>>8&255,255&e]);return Xe.box(Xe.types.mfhd,t)}static traf(e,t){let i=e.id,o=Xe.box(Xe.types.tfhd,new Uint8Array([0,0,0,0,i>>>24&255,i>>>16&255,i>>>8&255,255&i])),r=Xe.box(Xe.types.tfdt,new Uint8Array([0,0,0,0,t>>>24&255,t>>>16&255,t>>>8&255,255&t])),A=Xe.sdtp(e),s=Xe.trun(e,A.byteLength+16+16+8+16+8+8);return Xe.box(Xe.types.traf,o,r,s,A)}static sdtp(e){let t=new Uint8Array(5),i=e.flags;return t[4]=i.isLeading<<6|i.dependsOn<<4|i.isDependedOn<<2|i.hasRedundancy,Xe.box(Xe.types.sdtp,t)}static trun(e,t){let i=new Uint8Array(28);t+=36,i.set([0,0,15,1,0,0,0,1,t>>>24&255,t>>>16&255,t>>>8&255,255&t],0);let o=e.duration,r=e.size,A=e.flags,s=e.cts;return i.set([o>>>24&255,o>>>16&255,o>>>8&255,255&o,r>>>24&255,r>>>16&255,r>>>8&255,255&r,A.isLeading<<2|A.dependsOn,A.isDependedOn<<6|A.hasRedundancy<<4|A.isNonSync,0,0,s>>>24&255,s>>>16&255,s>>>8&255,255&s],12),Xe.box(Xe.types.trun,i)}static mdat(e){return Xe.box(Xe.types.mdat,e)}}Xe.init();class Ze{constructor(e){this.TAG="ExpGolomb",this._buffer=e,this._buffer_index=0,this._total_bytes=e.byteLength,this._total_bits=8*e.byteLength,this._current_word=0,this._current_word_bits_left=0}destroy(){this._buffer=null}_fillCurrentWord(){let e=this._total_bytes-this._buffer_index,t=Math.min(4,e),i=new Uint8Array(4);i.set(this._buffer.subarray(this._buffer_index,this._buffer_index+t)),this._current_word=new DataView(i.buffer).getUint32(0,!1),this._buffer_index+=t,this._current_word_bits_left=8*t}readBits(e){if(e<=this._current_word_bits_left){let t=this._current_word>>>32-e;return this._current_word<<=e,this._current_word_bits_left-=e,t}let t=this._current_word_bits_left?this._current_word:0;t>>>=32-this._current_word_bits_left;let i=e-this._current_word_bits_left;this._fillCurrentWord();let o=Math.min(i,this._current_word_bits_left),r=this._current_word>>>32-o;return this._current_word<<=o,this._current_word_bits_left-=o,t=t<>>e))return this._current_word<<=e,this._current_word_bits_left-=e,e;return this._fillCurrentWord(),e+this._skipLeadingZero()}readUEG(){let e=this._skipLeadingZero();return this.readBits(e+1)-1}readSEG(){let e=this.readUEG();return 1&e?e+1>>>1:-1*(e>>>1)}}class qe{static _ebsp2rbsp(e){let t=e,i=t.byteLength,o=new Uint8Array(i),r=0;for(let e=0;e=2&&3===t[e]&&0===t[e-1]&&0===t[e-2]||(o[r]=t[e],r++);return new Uint8Array(o.buffer,0,r)}static parseSPS(e){let t=qe._ebsp2rbsp(e),i=new Ze(t);i.readByte();let o=i.readByte();i.readByte();let r=i.readByte();i.readUEG();let A=qe.getProfileString(o),s=qe.getLevelString(r),n=1,a=420,d=[0,420,422,444],c=8;if((100===o||110===o||122===o||244===o||44===o||83===o||86===o||118===o||128===o||138===o||144===o)&&(n=i.readUEG(),3===n&&i.readBits(1),n<=3&&(a=d[n]),c=i.readUEG()+8,i.readUEG(),i.readBits(1),i.readBool())){let e=3!==n?8:12;for(let t=0;t0&&e<16?(v=t[e-1],w=o[e-1]):255===e&&(v=i.readByte()<<8|i.readByte(),w=i.readByte()<<8|i.readByte())}if(i.readBool()&&i.readBool(),i.readBool()&&(i.readBits(4),i.readBool()&&i.readBits(24)),i.readBool()&&(i.readUEG(),i.readUEG()),i.readBool()){let e=i.readBits(32),t=i.readBits(32);E=i.readBool(),B=t,C=2*e,S=B/C}}let R=1;1===v&&1===w||(R=v/w);let k=0,T=0;if(0===n)k=1,T=2-m;else{k=3===n?1:2,T=(1===n?2:1)*(2-m)}let I=16*(h+1),x=16*(p+1)*(2-m);I-=(g+f)*k,x-=(b+y)*T;let D=Math.ceil(I*R);return i.destroy(),i=null,{profile_string:A,level_string:s,bit_depth:c,ref_frames:u,chroma_format:a,chroma_format_string:qe.getChromaFormatString(a),frame_rate:{fixed:E,fps:S,fps_den:C,fps_num:B},sar_ratio:{width:v,height:w},codec_size:{width:I,height:x},present_size:{width:D,height:x}}}static _skipScalingList(e,t){let i=8,o=8,r=0;for(let A=0;A{this.mediaSourceOpen=!0,this.player.emit(T.mseSourceOpen)})),i(this.mediaSource,"sourceclose",(()=>{this.player.emit(T.mseSourceClose)})),e.debug.log("MediaSource","init")}destroy(){this.stop(),this.bufferList=[],this.mediaSource=null,this.mediaSourceOpen=!1,this.sourceBuffer=null,this.hasInit=!1,this.isInitInfo=!1,this.sequenceNumber=0,this.cacheTrack=null,this.timeInit=!1,this.off(),this.player.debug.log("MediaSource","destroy")}get state(){return this.mediaSource.readyState}get isStateOpen(){return this.state===te}get isStateClosed(){return this.state===ie}get isStateEnded(){return this.state===ee}get duration(){return this.mediaSource.duration}set duration(e){this.mediaSource.duration=e}decodeVideo(e,t,i){const o=this.player;if(this.hasInit)this._decodeVideo(e,t,i);else if(i&&0===e[1]){const r=15&e[0];if(o.video.updateVideoInfo({encTypeCode:r}),r===M)return void this.emit(x.mediaSourceH265NotSupport);o._times.decodeStart||(o._times.decodeStart=he()),this._decodeConfigurationRecord(e,t,i,r),this.hasInit=!0}}_doDecode(){const e=this.bufferList.shift();e&&this._decodeVideo(e.payload,e.ts,e.isIframe)}_decodeConfigurationRecord(e,t,i,o){let r=e.slice(5),A={};o===O?A=function(e){const t={},i=new DataView(e.buffer);let o=i.getUint8(0),r=i.getUint8(1);if(i.getUint8(2),i.getUint8(3),1!==o||0===r)return;const A=1+(3&i.getUint8(4));if(3!==A&&4!==A)return;let s=31&i.getUint8(5);if(0===s)return;let n=6;for(let o=0;o1&&(this.removeBuffer(n.buffered.start(0),n.buffered.end(0)),this.timeInit=!1),this.dropping&&s-this.cacheTrack.dts>1e3)this.dropping=!1,this.cacheTrack={};else if(this.cacheTrack&&s>this.cacheTrack.dts){let e=8+this.cacheTrack.size,i=new Uint8Array(e);i[0]=e>>>24&255,i[1]=e>>>16&255,i[2]=e>>>8&255,i[3]=255&e,i.set(Xe.types.mdat,4),i.set(this.cacheTrack.data,8),this.cacheTrack.duration=s-this.cacheTrack.dts;let r=Xe.moof(this.cacheTrack,this.cacheTrack.dts),A=new Uint8Array(r.byteLength+i.byteLength);A.set(r,0),A.set(i,r.byteLength),this.appendBuffer(A.buffer),o.handleRender(),o.updateStats({fps:!0,ts:t,buf:o.demux.delay}),o._times.videoStart||(o._times.videoStart=he(),o.handlePlayToRenderTimes())}else o.debug.log("MediaSource","timeInit set false , cacheTrack = {}"),this.timeInit=!1,this.cacheTrack={};this.cacheTrack.id=1,this.cacheTrack.sequenceNumber=++this.sequenceNumber,this.cacheTrack.size=A,this.cacheTrack.dts=s,this.cacheTrack.cts=0,this.cacheTrack.isKeyframe=i,this.cacheTrack.data=r,this.cacheTrack.flags={isLeading:0,dependsOn:i?2:1,isDependedOn:i?1:0,hasRedundancy:0,isNonSync:i?0:1},this.timeInit||1!==n.buffered.length||(o.debug.log("MediaSource","timeInit set true"),this.timeInit=!0,n.currentTime=n.buffered.end(0)),!this.isInitInfo&&n.videoWidth>0&&n.videoHeight>0&&(o.debug.log("MediaSource",`updateVideoInfo: ${n.videoWidth},${n.videoHeight}`),o.video.updateVideoInfo({width:n.videoWidth,height:n.videoHeight}),o.video.initCanvasViewSize(),this.isInitInfo=!0)}appendBuffer(e){const{debug:t,events:{proxy:i}}=this.player;null===this.sourceBuffer&&(this.sourceBuffer=this.mediaSource.addSourceBuffer($),i(this.sourceBuffer,"error",(e=>{this.player.emit(T.mseSourceBufferError,e)}))),!1===this.sourceBuffer.updating&&this.isStateOpen?this.sourceBuffer.appendBuffer(e):this.isStateClosed?this.player.emit(T.mseSourceBufferError,"mediaSource is not attached to video or mediaSource is closed"):this.isStateEnded?this.player.emit(T.mseSourceBufferError,"mediaSource is closed"):!0===this.sourceBuffer.updating&&this.player.emit(T.mseSourceBufferBusy)}stop(){this.isStateOpen&&this.sourceBuffer&&this.sourceBuffer.abort(),this.endOfStream()}dropSourceBuffer(e){const t=this.player.video.$videoElement;this.dropping=e,t.buffered.length>0&&t.buffered.end(0)-t.currentTime>1&&(t.currentTime=t.buffered.end(0))}removeBuffer(e,t){if(this.isStateOpen&&!1===this.sourceBuffer.updating)try{this.sourceBuffer.remove(e,t)}catch(e){console.error(e)}}endOfStream(){this.isStateOpen&&this.mediaSource.endOfStream()}}const _e=()=>"undefined"!=typeof navigator&&parseFloat((""+(/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))<10&&!window.MSStream,$e=()=>"wakeLock"in navigator;class et{constructor(e){if(this.player=e,this.enabled=!1,$e()){this._wakeLock=null;const e=()=>{null!==this._wakeLock&&"visible"===document.visibilityState&&this.enable()};document.addEventListener("visibilitychange",e),document.addEventListener("fullscreenchange",e)}else _e()?this.noSleepTimer=null:(this.noSleepVideo=document.createElement("video"),this.noSleepVideo.setAttribute("title","No Sleep"),this.noSleepVideo.setAttribute("playsinline",""),this._addSourceToVideo(this.noSleepVideo,"webm","data:video/webm;base64,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"),this._addSourceToVideo(this.noSleepVideo,"mp4","data:video/mp4;base64,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"),this.noSleepVideo.addEventListener("loadedmetadata",(()=>{this.noSleepVideo.duration<=1?this.noSleepVideo.setAttribute("loop",""):this.noSleepVideo.addEventListener("timeupdate",(()=>{this.noSleepVideo.currentTime>.5&&(this.noSleepVideo.currentTime=Math.random())}))})))}_addSourceToVideo(e,t,i){var o=document.createElement("source");o.src=i,o.type=`video/${t}`,e.appendChild(o)}get isEnabled(){return this.enabled}enable(){const e=this.player.debug;if($e())return navigator.wakeLock.request("screen").then((t=>{this._wakeLock=t,this.enabled=!0,e.log("wakeLock","Wake Lock active."),this._wakeLock.addEventListener("release",(()=>{e.log("wakeLock","Wake Lock released.")}))})).catch((t=>{throw this.enabled=!1,e.error("wakeLock",`${t.name}, ${t.message}`),t}));if(_e())return this.disable(),this.noSleepTimer=window.setInterval((()=>{document.hidden||(window.location.href=window.location.href.split("#")[0],window.setTimeout(window.stop,0))}),15e3),this.enabled=!0,Promise.resolve();return this.noSleepVideo.play().then((e=>(this.enabled=!0,e))).catch((e=>{throw this.enabled=!1,e}))}disable(){const e=this.player.debug;$e()?(this._wakeLock&&this._wakeLock.release(),this._wakeLock=null):_e()?this.noSleepTimer&&(e.warn("wakeLock","NoSleep now disabled for older iOS devices."),window.clearInterval(this.noSleepTimer),this.noSleepTimer=null):this.noSleepVideo.pause(),this.enabled=!1}}class tt extends Be{constructor(e,t){var i;super(),this.$container=e,this._opt=Object.assign({},d,t),this.debug=new ne(this),this._opt.useWCS&&(this._opt.useWCS="VideoEncoder"in window),this._opt.useMSE&&(this._opt.useMSE=window.MediaSource&&window.MediaSource.isTypeSupported($)),this._opt.useMSE?(this._opt.useWCS&&this.debug.log("Player","useWCS set true->false"),this._opt.forceNoOffscreen||this.debug.log("Player","forceNoOffscreen set false->true"),this._opt.useWCS=!1,this._opt.forceNoOffscreen=!0):this._opt.useWCS,this._opt.forceNoOffscreen||("undefined"==typeof OffscreenCanvas?(this._opt.forceNoOffscreen=!0,this._opt.useOffscreen=!1):this._opt.useOffscreen=!0),this._opt.hasAudio||(this._opt.operateBtns.audio=!1),this._opt.hasControl=this._hasControl(),this._loading=!1,this._playing=!1,this._hasLoaded=!1,this._checkHeartTimeout=null,this._checkLoadingTimeout=null,this._startBpsTime=null,this._isPlayingBeforePageHidden=!1,this._stats={buf:0,fps:0,abps:0,vbps:0,ts:0},this._times={playInitStart:"",playStart:"",streamStart:"",streamResponse:"",demuxStart:"",decodeStart:"",videoStart:"",playTimestamp:"",streamTimestamp:"",streamResponseTimestamp:"",demuxTimestamp:"",decodeTimestamp:"",videoTimestamp:"",allTimestamp:""},this._videoTimestamp=0,this._audioTimestamp=0,i=this,Object.defineProperty(i,"rect",{get:()=>{const e=i.$container.getBoundingClientRect();return e.width=Math.max(e.width,i.$container.clientWidth),e.height=Math.max(e.height,i.$container.clientHeight),e}}),["bottom","height","left","right","top","width"].forEach((e=>{Object.defineProperty(i,e,{get:()=>i.rect[e]})})),this.events=new ae(this),this.video=new Te(this),this._opt.hasAudio&&(this.audio=new xe(this)),this.recorder=new Ve(this),this._onlyMseOrWcsVideo()?this.loaded=!0:this.decoderWorker=new Oe(this),this.stream=null,this.demux=null,this._opt.useWCS&&(this.webcodecsDecoder=new Je(this)),this._opt.useMSE&&(this.mseDecoder=new Ke(this)),this.control=new He(this),this.keepScreenOn=new et(this),(e=>{try{const t=()=>{e.emit(I.fullscreen,e.fullscreen),e.fullscreen?e._opt.useMSE&&e.resize():e.resize()};de.on("change",t),e.events.destroys.push((()=>{de.off("change",t)}))}catch(e){}if(e.on(T.decoderWorkerInit,(()=>{e.debug.log("player","has loaded"),e.loaded=!0})),e.on(T.play,(()=>{e.loading=!1})),e.on(T.fullscreen,(t=>{if(t)try{de.request(e.$container).then((()=>{})).catch((t=>{e.webFullscreen=!0}))}catch(t){e.webFullscreen=!0}else try{de.exit().then((()=>{})).catch((()=>{e.webFullscreen=!1}))}catch(t){e.webFullscreen=!1}})),e.on(T.webFullscreen,(t=>{t?e.$container.classList.add("jessibuca-fullscreen-web"):e.$container.classList.remove("jessibuca-fullscreen-web")})),e.on(T.resize,(()=>{e.video.resize()})),e._opt.debug){const t=[T.timeUpdate];Object.keys(T).forEach((i=>{e.on(T[i],(o=>{t.includes(i)||e.debug.log("player events",T[i],o)}))})),Object.keys(x).forEach((t=>{e.on(x[t],(i=>{e.debug.log("player event error",x[t],i)}))}))}})(this),(e=>{const{_opt:t,debug:i,events:{proxy:o}}=e;t.supportDblclickFullscreen&&o(e.$container,"dblclick",(()=>{e.fullscreen=!e.fullscreen})),o(document,"visibilitychange",(()=>{t.hiddenAutoPause&&(i.log("visibilitychange",document.visibilityState,e._isPlayingBeforePageHidden),"visible"===document.visibilityState?e._isPlayingBeforePageHidden&&e.play():(e._isPlayingBeforePageHidden=e.playing,e.playing&&e.pause()))})),o(window,"fullscreenchange",(()=>{null!==e.keepScreenOn&&"visible"===document.visibilityState&&e.enableWakeLock()}))})(this),this._opt.useWCS&&this.debug.log("Player","use WCS"),this._opt.useMSE&&this.debug.log("Player","use MSE"),this._opt.useOffscreen&&this.debug.log("Player","use offscreen"),this.debug.log("Player options",this._opt)}destroy(){this._loading=!1,this._playing=!1,this._hasLoaded=!1,this._times={playInitStart:"",playStart:"",streamStart:"",streamResponse:"",demuxStart:"",decodeStart:"",videoStart:"",playTimestamp:"",streamTimestamp:"",streamResponseTimestamp:"",demuxTimestamp:"",decodeTimestamp:"",videoTimestamp:"",allTimestamp:""},this.decoderWorker&&(this.decoderWorker.destroy(),this.decoderWorker=null),this.video&&(this.video.destroy(),this.video=null),this.audio&&(this.audio.destroy(),this.audio=null),this.stream&&(this.stream.destroy(),this.stream=null),this.recorder&&(this.recorder.destroy(),this.recorder=null),this.control&&(this.control.destroy(),this.control=null),this.webcodecsDecoder&&(this.webcodecsDecoder.destroy(),this.webcodecsDecoder=null),this.mseDecoder&&(this.mseDecoder.destroy(),this.mseDecoder=null),this.demux&&(this.demux.destroy(),this.demux=null),this.events&&(this.events.destroy(),this.events=null),this.clearCheckHeartTimeout(),this.clearCheckLoadingTimeout(),this.releaseWakeLock(),this.keepScreenOn=null,this.resetStats(),this._audioTimestamp=0,this._videoTimestamp=0,this.emit("destroy"),this.off(),this.debug.log("play","destroy end")}set fullscreen(e){ye()?(this.emit(T.webFullscreen,e),setTimeout((()=>{this.updateOption({rotate:e?270:0}),this.resize()}),10)):this.emit(T.fullscreen,e)}get fullscreen(){return document.isFullScreen||document.mozIsFullScreen||document.webkitIsFullScreen||this.webFullscreen}set webFullscreen(e){this.emit(T.webFullscreen,e)}get webFullscreen(){return this.$container.classList.contains("jessibuca-fullscreen-web")}set loaded(e){this._hasLoaded=e}get loaded(){return this._hasLoaded}set playing(e){e&&(this.loading=!1),this.playing!==e&&(this._playing=e,this.emit(T.playing,e),this.emit(T.volumechange,this.volume),e?this.emit(T.play):this.emit(T.pause))}get playing(){return this._playing}get volume(){return this.audio&&this.audio.volume||0}set volume(e){this.audio&&this.audio.setVolume(e)}set loading(e){this.loading!==e&&(this._loading=e,this.emit(T.loading,this._loading))}get loading(){return this._loading}set recording(e){this.playing&&(e?this.recorder.startRecord():this.recorder.stopRecordAndSave())}get recording(){return this.recorder&&this.recorder.recording}set audioTimestamp(e){null!==e&&(this._audioTimestamp=e)}get audioTimestamp(){return this._audioTimestamp}set videoTimestamp(e){null!==e&&(this._videoTimestamp=e,this._opt.useWCS||this._opt.useMSE||this.audioTimestamp&&this.videoTimestamp&&this.audio&&this.audio.emit(T.videoSyncAudio,{audioTimestamp:this.audioTimestamp,videoTimestamp:this.videoTimestamp,diff:this.audioTimestamp-this.videoTimestamp}))}get videoTimestamp(){return this._videoTimestamp}updateOption(e){this._opt=Object.assign({},this._opt,e)}init(){return new Promise(((e,t)=>{this.stream||(this.stream=new Le(this)),this.demux||(this.demux=new Ge(this)),this._opt.useWCS&&(this.webcodecsDecoder||(this.webcodecsDecoder=new Je(this))),this._opt.useMSE&&(this.mseDecoder||(this.mseDecoder=new Ke(this))),this.decoderWorker||this._onlyMseOrWcsVideo()?e():(this.decoderWorker=new Oe(this),this.once(T.decoderWorkerInit,(()=>{e()})))}))}play(e){return new Promise(((t,i)=>{if(!e&&!this._opt.url)return i();this.loading=!0,this.playing=!1,this._times.playInitStart=he(),e||(e=this._opt.url),this._opt.url=e,this.clearCheckHeartTimeout(),this.init().then((()=>{this._times.playStart=he(),this._opt.isNotMute&&this.mute(!1),this.webcodecsDecoder&&this.webcodecsDecoder.once(x.webcodecsH265NotSupport,(()=>{this.emit(x.webcodecsH265NotSupport),this._opt.autoWasm||this.emit(T.error,x.webcodecsH265NotSupport)})),this.mseDecoder&&this.mseDecoder.once(x.mediaSourceH265NotSupport,(()=>{this.emit(x.mediaSourceH265NotSupport),this._opt.autoWasm||this.emit(T.error,x.mediaSourceH265NotSupport)})),this.enableWakeLock(),this.stream.fetchStream(e),this.checkLoadingTimeout(),this.stream.once(x.fetchError,(e=>{i(e)})),this.stream.once(x.websocketError,(e=>{i(e)})),this.stream.once(T.streamSuccess,(()=>{t(),this._times.streamResponse=he(),this._opt.useMSE&&this.video.play()}))})).catch((e=>{i(e)}))}))}close(){return new Promise(((e,t)=>{this._close().then((()=>{this.video.clearView(),e()}))}))}_close(){return new Promise(((e,t)=>{this.stream&&(this.stream.destroy(),this.stream=null),this.demux&&(this.demux.destroy(),this.demux=null),this.decoderWorker&&(this.decoderWorker.destroy(),this.decoderWorker=null),this.webcodecsDecoder&&(this.webcodecsDecoder.destroy(),this.webcodecsDecoder=null),this.mseDecoder&&(this.mseDecoder.destroy(),this.mseDecoder=null),this.clearCheckHeartTimeout(),this.clearCheckLoadingTimeout(),this.playing=!1,this.loading=!1,this.recording=!1,this.audio&&this.audio.pause(),this.releaseWakeLock(),this.resetStats(),this._audioTimestamp=0,this._videoTimestamp=0,this._times={playInitStart:"",playStart:"",streamStart:"",streamResponse:"",demuxStart:"",decodeStart:"",videoStart:"",playTimestamp:"",streamTimestamp:"",streamResponseTimestamp:"",demuxTimestamp:"",decodeTimestamp:"",videoTimestamp:"",allTimestamp:""},setTimeout((()=>{e()}),0)}))}pause(e){return e?this.close():this._close()}mute(e){this.audio&&this.audio.mute(e)}resize(){this.video.resize()}startRecord(e,t){this.recording||(this.recorder.setFileName(e,t),this.recording=!0)}stopRecordAndSave(){this.recording&&(this.recording=!1)}_hasControl(){let e=!1,t=!1;return Object.keys(this._opt.operateBtns).forEach((e=>{this._opt.operateBtns[e]&&(t=!0)})),(this._opt.showBandwidth||this._opt.text||t)&&(e=!0),e}_onlyMseOrWcsVideo(){return!1===this._opt.hasAudio&&(this._opt.useMSE||this._opt.useWCS&&!this._opt.useOffscreen)}checkHeart(){this.clearCheckHeartTimeout(),this.checkHeartTimeout()}checkHeartTimeout(){this._checkHeartTimeout=setTimeout((()=>{this.pause(!1).then((()=>{this.emit(T.timeout,T.delayTimeout),this.emit(T.delayTimeout)}))}),1e3*this._opt.heartTimeout)}clearCheckHeartTimeout(){this._checkHeartTimeout&&(clearTimeout(this._checkHeartTimeout),this._checkHeartTimeout=null)}checkLoadingTimeout(){this._checkLoadingTimeout=setTimeout((()=>{this.pause(!1).then((()=>{this.emit(T.timeout,T.loadingTimeout),this.emit(T.loadingTimeout)}))}),1e3*this._opt.loadingTimeout)}clearCheckLoadingTimeout(){this._checkLoadingTimeout&&(clearTimeout(this._checkLoadingTimeout),this._checkLoadingTimeout=null)}handleRender(){this.loading&&(this.emit(T.start),this.loading=!1,this.clearCheckLoadingTimeout()),this.playing||(this.playing=!0),this.checkHeart()}updateStats(e){e=e||{},this._startBpsTime||(this._startBpsTime=he()),Ee(e.ts)&&(this._stats.ts=e.ts),Ee(e.buf)&&(this._stats.buf=e.buf),e.fps&&(this._stats.fps+=1),e.abps&&(this._stats.abps+=e.abps),e.vbps&&(this._stats.vbps+=e.vbps);const t=he();t-this._startBpsTime<1e3||(this.emit(T.stats,this._stats),this.emit(T.performance,function(e){let t=0;return e>=24?t=2:e>=15&&(t=1),t}(this._stats.fps)),this._stats.fps=0,this._stats.abps=0,this._stats.vbps=0,this._startBpsTime=t)}resetStats(){this._startBpsTime=null,this._stats={buf:0,fps:0,abps:0,vbps:0,ts:0}}enableWakeLock(){this._opt.keepScreenOn&&this.keepScreenOn.enable()}releaseWakeLock(){this._opt.keepScreenOn&&this.keepScreenOn.disable()}handlePlayToRenderTimes(){const e=this._times;e.playTimestamp=e.playStart-e.playInitStart,e.streamTimestamp=e.streamStart-e.playStart,e.streamResponseTimestamp=e.streamResponse-e.streamStart,e.demuxTimestamp=e.demuxStart-e.streamResponse,e.decodeTimestamp=e.decodeStart-e.demuxStart,e.videoTimestamp=e.videoStart-e.decodeStart,e.allTimestamp=e.videoStart-e.playInitStart,this.emit(T.playToRenderTimes,e)}}class it extends Be{constructor(e){super();let t=e,i=e.container;if("string"==typeof e.container&&(i=document.querySelector(e.container)),!i)throw new Error("Jessibuca need container option");i.classList.add("jessibuca-container"),delete t.container,Ee(t.videoBuffer)&&(t.videoBuffer=1e3*Number(t.videoBuffer)),Ee(t.timeout)&&(Se(t.loadingTimeout)&&(t.loadingTimeout=t.timeout),Se(t.heartTimeout)&&(t.heartTimeout=t.timeout)),this._opt=t,this.$container=i,this._loadingTimeoutReplayTimes=0,this._heartTimeoutReplayTimes=0,this.events=new ae(this),this._initPlayer(i,t)}destroy(){this.events&&(this.events.destroy(),this.events=null),this.player&&(this.player.destroy(),this.player=null),this.$container=null,this._opt=null,this._loadingTimeoutReplayTimes=0,this._heartTimeoutReplayTimes=0,this.off()}_initPlayer(e,t){this.player=new tt(e,t),this._bindEvents()}_resetPlayer(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.player.destroy(),this.player=null;const t=Object.assign(this._opt,e);this._initPlayer(this.$container,t)}_bindEvents(){Object.keys(I).forEach((e=>{this.player.on(I[e],(t=>{this.emit(e,t)}))}))}setDebug(e){this.player.updateOption({isDebug:!!e})}mute(){this.player.mute(!0)}cancelMute(){this.player.mute(!1)}setVolume(e){this.player.volume=e}audioResume(){this.player.audio&&this.player.audio.audioEnabled(!0)}setTimeout(e){e=Number(e),this.player.updateOption({timeout:e,loadingTimeout:e,heartTimeout:e})}setScaleMode(e){let t={isFullResize:!1,isResize:!1};switch(e=Number(e)){case N:t.isFullResize=!1,t.isResize=!1;break;case P:t.isFullResize=!1,t.isResize=!0;break;case z:t.isFullResize=!0,t.isResize=!0}this.player.updateOption(t),this.resize()}pause(){return this.player.pause()}close(){return this._opt.url="",this.player.close()}clearView(){this.player.video.clearView()}play(e){return new Promise(((t,i)=>{if(!e&&!this._opt.url)return this.emit(T.error,x.playError),void i();if(e){if(!this._opt.url)return this._play(e);e===this._opt.url?this.player.playing?t():(this.clearView(),this.player.play(this._opt.url).then((()=>{t()})).catch((()=>{this.player.pause().then((()=>{i()}))}))):this.player.pause().then((()=>(this.clearView(),this._play(e)))).catch((()=>{i()}))}else this.player.play(this._opt.url).then((()=>{t()})).catch((()=>{this.player.pause().then((()=>{i()}))}))}))}_play(e){return new Promise(((t,i)=>{this._opt.url=e;const o=0===e.indexOf("http"),r=o?s:A,d=o||-1!==e.indexOf(".flv")||this._opt.isFlv?n:a;this.player.updateOption({protocol:r,demuxType:d}),this.player.once(x.mediaSourceH265NotSupport,(()=>{this.close().then((()=>{this.player._opt.autoWasm&&(this.player.debug.log("Jessibuca","auto wasm [mse-> wasm] reset player and play"),this._resetPlayer({useMSE:!1}),this.play(e).then((()=>{this.player.debug.log("Jessibuca","auto wasm [mse-> wasm] reset player and play success")})).catch((()=>{this.player.debug.log("Jessibuca","auto wasm [mse-> wasm] reset player and play error")})))}))})),this.player.once(x.webcodecsH265NotSupport,(()=>{this.close().then((()=>{this.player._opt.autoWasm&&(this.player.debug.log("Jessibuca","auto wasm [wcs-> wasm] reset player and play"),this._resetPlayer({useWCS:!1}),this.play(e).then((()=>{this.player.debug.log("Jessibuca","auto wasm [wcs-> wasm] reset player and play success")})).catch((()=>{this.player.debug.log("Jessibuca","auto wasm [wcs-> wasm] reset player and play error")})))}))})),this.player.once(x.wasmDecodeError,(()=>{this.player._opt.wasmDecodeErrorReplay&&this.close().then((()=>{this.player.debug.log("Jessibuca","wasm decode error and reset player and play"),this._resetPlayer({useWCS:!1}),this.play(e).then((()=>{this.player.debug.log("Jessibuca","wasm decode error and reset player and play success")})).catch((()=>{this.player.debug.log("Jessibuca","wasm decode error and reset player and play error")}))}))})),this.player.once(T.delayTimeout,(()=>{this.player._opt.heartTimeoutReplay&&this._heartTimeoutReplayTimes{this._heartTimeoutReplayTimes=0})).catch((()=>{})))})),this.player.once(T.loadingTimeout,(()=>{this.player._opt.loadingTimeoutReplay&&this._loadingTimeoutReplayTimes{this._loadingTimeoutReplayTimes=0})).catch((()=>{})))})),this.hasLoaded()?this.player.play(e).then((()=>{t()})).catch((()=>{this.player.pause().then((()=>{i()}))})):this.player.once(T.decoderWorkerInit,(()=>{this.player.play(e).then((()=>{t()})).catch((()=>{this.player.pause().then((()=>{i()}))}))}))}))}resize(){this.player.resize()}setBufferTime(e){e=Number(e),this.player.updateOption({videoBuffer:1e3*e}),this.player.decoderWorker&&this.player.decoderWorker.updateWorkConfig({key:"videoBuffer",value:1e3*e})}setRotate(e){e=parseInt(e,10);this._opt.rotate!==e&&-1!==[0,90,270].indexOf(e)&&(this.player.updateOption({rotate:e}),this.resize())}hasLoaded(){return this.player.loaded}setKeepScreenOn(){this.player.updateOption({keepScreenOn:!0})}setFullscreen(e){const t=!!e;this.player.fullscreen!==t&&(this.player.fullscreen=t)}screenshot(e,t,i,o){return this.player.video.screenshot(e,t,i,o)}startRecord(e,t){return new Promise(((i,o)=>{this.player.playing?(this.player.startRecord(e,t),i()):o()}))}stopRecordAndSave(){this.player.recording&&this.player.stopRecordAndSave()}isPlaying(){return this.player.playing}isMute(){return!this.player.audio||this.player.audio.isMute}isRecording(){return this.player.recorder.recording}}return r(it,"ERROR",x),r(it,"TIMEOUT",{loadingTimeout:T.loadingTimeout,delayTimeout:T.delayTimeout}),window.Jessibuca=it,it})); diff --git a/web_src/static/js/jessibuca/logo.png b/web_src/static/js/jessibuca/logo.png deleted file mode 100644 index 751de2a4..00000000 Binary files a/web_src/static/js/jessibuca/logo.png and /dev/null differ diff --git a/web_src/static/js/mapConfig.js b/web_src/static/js/mapConfig.js new file mode 100644 index 00000000..60427e88 --- /dev/null +++ b/web_src/static/js/mapConfig.js @@ -0,0 +1,19 @@ +// map组件全局参数, 注释此内容可以关闭地图功能 +window.mapParam = { + // 开启/关闭地图功能 + enable: true, + // 坐标系 GCJ-02 WGS-84, + coordinateSystem: "GCJ-02", + // 地图瓦片地址 + tilesUrl: "http://webrd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8", + // 瓦片大小 + tileSize: 256, + // 默认层级 + zoom:10, + // 默认地图中心点 + center:[116.41020, 39.915119], + // 地图最大层级 + maxZoom:18, + // 地图最小层级 + minZoom: 3 +}